From 6a701b2679883823f04fed10f301fefcca1adaec Mon Sep 17 00:00:00 2001 From: caszu <109808097+caszuu@users.noreply.github.com> Date: Tue, 23 Dec 2025 15:37:08 +0100 Subject: [PATCH 001/117] fix android SetWindowState (#5424) --- src/platforms/rcore_android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 7b8d3e052..20a85a6a4 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -360,7 +360,7 @@ void SetWindowState(unsigned int flags) if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead"); // State change: FLAG_WINDOW_ALWAYS_RUN - if (!FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN); + if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN); } // Clear window configuration state flags From 0a4583ca5468e48a3e7b7ea9ca4a8055272e524e Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Tue, 23 Dec 2025 11:10:55 -0500 Subject: [PATCH 002/117] [rl_gputex.h] Possibly fixed the swizzling in `rl_load_dds_from_memory()` function (#5422) * Possibly fixed the swizzling bug * Removed examples, and generation. --- src/external/rl_gputex.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index 29500f3cf..78d618db5 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -308,7 +308,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ unsigned char alpha = 0; // NOTE: Data comes as A1R5G5B5, it must be reordered to R5G5B5A1 - for (int i = 0; i < image_pixel_size; i++) + for (int i = 0; i < data_size/sizeof(unsigned short); i++) { alpha = ((unsigned short *)image_data)[i] >> 15; ((unsigned short *)image_data)[i] = ((unsigned short *)image_data)[i] << 1; @@ -328,7 +328,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ unsigned char alpha = 0; // NOTE: Data comes as A4R4G4B4, it must be reordered R4G4B4A4 - for (int i = 0; i < image_pixel_size; i++) + for (int i = 0; i < data_size/sizeof(unsigned short); i++) { alpha = ((unsigned short *)image_data)[i] >> 12; ((unsigned short *)image_data)[i] = ((unsigned short *)image_data)[i] << 4; @@ -362,7 +362,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ // NOTE: Data comes as A8R8G8B8, it must be reordered R8G8B8A8 (view next comment) // DirecX understand ARGB as a 32bit DWORD but the actual memory byte alignment is BGRA // So, we must realign B8G8R8A8 to R8G8B8A8 - for (int i = 0; i < image_pixel_size*4; i += 4) + for (int i = 0; i < data_size; i += 4) { blue = ((unsigned char *)image_data)[i]; ((unsigned char *)image_data)[i] = ((unsigned char *)image_data)[i + 2]; From ddb827fb6faa963a5c67dcc79d17d2d65e77c5e7 Mon Sep 17 00:00:00 2001 From: Kivi <35783191+KiviTK@users.noreply.github.com> Date: Wed, 24 Dec 2025 08:59:51 +0100 Subject: [PATCH 003/117] Fixed LoadCodepoints declaring a new local variable shadowing `codpoints` (#5430) --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index 7c25fde0b..e4b439d28 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -2090,7 +2090,7 @@ int *LoadCodepoints(const char *text, int *count) int textLength = TextLength(text); // Allocate a big enough buffer to store as many codepoints as text bytes - int *codepoints = (int *)RL_CALLOC(textLength, sizeof(int)); + codepoints = (int *)RL_CALLOC(textLength, sizeof(int)); int codepointSize = 0; for (int i = 0; i < textLength; codepointCount++) From a1e84caa8c26b36d6bfbc4a64b731fdeae1dacf2 Mon Sep 17 00:00:00 2001 From: Krzysztof Szenk Date: Wed, 24 Dec 2025 09:04:41 +0100 Subject: [PATCH 004/117] RGFW also requires RGBA8 images as window icons, as raylib already reports in raylib.h (#5431) --- src/platforms/rcore_desktop_rgfw.c | 52 ++++++++---------------------- 1 file changed, 13 insertions(+), 39 deletions(-) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 2671538d8..39ac8fb32 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -522,46 +522,15 @@ void ClearWindowState(unsigned int flags) } } -int RGFW_formatToChannels(int format) -{ - switch (format) - { - case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: - case PIXELFORMAT_UNCOMPRESSED_R16: // 16 bpp (1 channel - half float) - case PIXELFORMAT_UNCOMPRESSED_R32: // 32 bpp (1 channel - float) - return 1; - case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: // 8*2 bpp (2 channels) - case PIXELFORMAT_UNCOMPRESSED_R5G6B5: // 16 bpp - case PIXELFORMAT_UNCOMPRESSED_R8G8B8: // 24 bpp - case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: // 16 bpp (1 bit alpha) - case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: // 16 bpp (4 bit alpha) - case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: // 32 bpp - return 2; - case PIXELFORMAT_UNCOMPRESSED_R32G32B32: // 32*3 bpp (3 channels - float) - case PIXELFORMAT_UNCOMPRESSED_R16G16B16: // 16*3 bpp (3 channels - half float) - case PIXELFORMAT_COMPRESSED_DXT1_RGB: // 4 bpp (no alpha) - case PIXELFORMAT_COMPRESSED_ETC1_RGB: // 4 bpp - case PIXELFORMAT_COMPRESSED_ETC2_RGB: // 4 bpp - case PIXELFORMAT_COMPRESSED_PVRT_RGB: // 4 bpp - return 3; - case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: // 32*4 bpp (4 channels - float) - case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: // 16*4 bpp (4 channels - half float) - case PIXELFORMAT_COMPRESSED_DXT1_RGBA: // 4 bpp (1 bit alpha) - case PIXELFORMAT_COMPRESSED_DXT3_RGBA: // 8 bpp - case PIXELFORMAT_COMPRESSED_DXT5_RGBA: // 8 bpp - case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: // 8 bpp - case PIXELFORMAT_COMPRESSED_PVRT_RGBA: // 4 bpp - case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: // 8 bpp - case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: // 2 bpp - return 4; - default: return 4; - } -} - // Set icon for window void SetWindowIcon(Image image) { - RGFW_window_setIcon(platform.window, (u8 *)image.data, RGFW_AREA(image.width, image.height), RGFW_formatToChannels(image.format)); + if (image.format != PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) + { + TRACELOG(LOG_WARNING, "RGFW: Window icon image must be in R8G8B8A8 pixel format"); + return; + } + RGFW_window_setIcon(platform.window, (u8 *)image.data, RGFW_AREA(image.width, image.height), 4); } // Set icon for window @@ -578,12 +547,17 @@ void SetWindowIcons(Image *images, int count) for (int i = 0; i < count; i++) { + if (images[i].format != PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) + { + TRACELOG(LOG_WARNING, "RGFW: Window icon image must be in R8G8B8A8 pixel format"); + continue; + } if ((bigIcon == NULL) || ((images[i].width > bigIcon->width) && (images[i].height > bigIcon->height))) bigIcon = &images[i]; if ((smallIcon == NULL) || ((images[i].width < smallIcon->width) && (images[i].height > smallIcon->height))) smallIcon = &images[i]; } - if (smallIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)smallIcon->data, RGFW_AREA(smallIcon->width, smallIcon->height), RGFW_formatToChannels(smallIcon->format), RGFW_iconWindow); - if (bigIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)bigIcon->data, RGFW_AREA(bigIcon->width, bigIcon->height), RGFW_formatToChannels(bigIcon->format), RGFW_iconTaskbar); + if (smallIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)smallIcon->data, RGFW_AREA(smallIcon->width, smallIcon->height), 4, RGFW_iconWindow); + if (bigIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)bigIcon->data, RGFW_AREA(bigIcon->width, bigIcon->height), 4, RGFW_iconTaskbar); } } From 05f42aa119d53049f92aae4e60c3f325d4f52a6b Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 24 Dec 2025 18:02:04 +0100 Subject: [PATCH 005/117] Update core_highdpi_testbed.c --- examples/core/core_highdpi_testbed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_highdpi_testbed.c b/examples/core/core_highdpi_testbed.c index 6a036bbfc..bf103fb31 100644 --- a/examples/core/core_highdpi_testbed.c +++ b/examples/core/core_highdpi_testbed.c @@ -27,7 +27,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - SetConfigFlags(FLAG_WINDOW_HIGHDPI | FLAG_WINDOW_RESIZABLE); + SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_WINDOW_HIGHDPI); InitWindow(screenWidth, screenHeight, "raylib [core] example - highdpi testbed"); Vector2 scaleDpi = GetWindowScaleDPI(); From ced84333a9f7647f039b568e75af546b30e8a986 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 24 Dec 2025 18:02:24 +0100 Subject: [PATCH 006/117] Update rl_gputex.h --- src/external/rl_gputex.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index 78d618db5..9c1092695 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -339,7 +339,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ } } } - else if ((header->ddspf.flags == 0x40) && (header->ddspf.rgb_bit_count == 24)) // DDS_RGB, no compressed + else if ((header->ddspf.flags == 0x40) && (header->ddspf.rgb_bit_count == 24)) // DDS_RGB, no compressed { int data_size = image_pixel_size*3*sizeof(unsigned char); if (header->mipmap_count > 1) data_size = data_size + data_size/3; From 9103f6e0557f615c2295febff69c86a80fc0d2b9 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 24 Dec 2025 18:58:20 +0100 Subject: [PATCH 007/117] ADDED: New platform backend for Web: `Emscripten`, not dependant on GLFW.js -WIP- --- src/platforms/rcore_web_emscripten.c | 1701 ++++++++++++++++++++++++++ 1 file changed, 1701 insertions(+) create mode 100644 src/platforms/rcore_web_emscripten.c diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c new file mode 100644 index 000000000..1ed719631 --- /dev/null +++ b/src/platforms/rcore_web_emscripten.c @@ -0,0 +1,1701 @@ +/********************************************************************************************** +* +* rcore_web_emscripten - Functions to manage window, graphics device and inputs +* +* PLATFORM: WEB - EMSCRIPTEN +* - HTML5 (WebAssembly) +* +* LIMITATIONS: +* - TBD +* +* POSSIBLE IMPROVEMENTS: +* - TBD +* +* ADDITIONAL NOTES: +* - TRACELOG() function is located in raylib [utils] module +* +* CONFIGURATION: +* #define RCORE_PLATFORM_CUSTOM_FLAG +* Custom flag for rcore on target platform -not used- +* +* DEPENDENCIES: +* - emscripten: Allow interaction between browser API and C +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2025 Ramon Santamaria (@raysan5) and contributors +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#include // Emscripten functionality for C +#include // Emscripten HTML5 library + +#include // Required for: timespec, nanosleep(), select() - POSIX + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#if (_POSIX_C_SOURCE < 199309L) + #undef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext. +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef struct { + char canvasId[64]; // Current canvas id + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE glContext; // OpenGL context + unsigned int *pixels; // Pointer to pixel data buffer (RGBA 32bit format) +} PlatformData; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static const char cursorLUT[11][12] = { + "default", // 0 MOUSE_CURSOR_DEFAULT + "default", // 1 MOUSE_CURSOR_ARROW + "text", // 2 MOUSE_CURSOR_IBEAM + "crosshair", // 3 MOUSE_CURSOR_CROSSHAIR + "pointer", // 4 MOUSE_CURSOR_POINTING_HAND + "ew-resize", // 5 MOUSE_CURSOR_RESIZE_EW + "ns-resize", // 6 MOUSE_CURSOR_RESIZE_NS + "nwse-resize", // 7 MOUSE_CURSOR_RESIZE_NWSE + "nesw-resize", // 8 MOUSE_CURSOR_RESIZE_NESW + "move", // 9 MOUSE_CURSOR_RESIZE_ALL + "not-allowed" // 10 MOUSE_CURSOR_NOT_ALLOWED +}; + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +void ClosePlatform(void); // Close platform + +// Emscripten window callback events +static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData); +static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData); +static EM_BOOL EmscriptenVisibilityChangeCallback(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData); +static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData); +// TODO: Implement GLFW3 alternative for drop callback, runs when drop files into browser/canvas +//static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); + +// Emscripten input callback events +static EM_BOOL EmscriptenKeyboardCallback(int eventType, const EmscriptenKeyboardEvent *keyboardEvent, void *userData); +static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); +static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); +static EM_BOOL EmscriptenMouseWheelCallback(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData); +static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData); +static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); +static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData); + +// JS: Set the canvas id provided by the module configuration +EM_JS(void, SetCanvasIdJs, (char *out, int outSize), { + var canvasId = "#" + Module.canvas.id; + stringToUTF8(canvasId, out, outSize); +}); + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Window and Graphics Device +//---------------------------------------------------------------------------------- + +// Check if application should close +// This will always return false on a web-build as web builds have no control over this functionality +// Sleep is handled in EndDrawing() for synchronous code +bool WindowShouldClose(void) +{ + // Emscripten Asyncify is required to run synchronous code in asynchronous JS + // REF: https://emscripten.org/docs/porting/asyncify.html + + // WindowShouldClose() is not called on a web-ready raylib application if using emscripten_set_main_loop() + // and encapsulating one frame execution on a UpdateDrawFrame() function, + // allowing the browser to manage execution asynchronously + + // Optionally we can manage the time we give-control-back-to-browser if required, + // but it seems below line could generate stuttering on some browsers + emscripten_sleep(12); + + return false; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + bool enterFullscreen = false; + + const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0); + if (wasFullscreen) + { + if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) enterFullscreen = false; + else if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) enterFullscreen = true; + else + { + const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0); + const int canvasStyleWidth = EM_ASM_INT( { return parseInt(Module.canvas.style.width); }, 0); + if (canvasStyleWidth > canvasWidth) enterFullscreen = false; + else enterFullscreen = true; + } + + EM_ASM(document.exitFullscreen();); + + CORE.Window.fullscreen = false; + FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); + FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE); + } + else enterFullscreen = true; + + if (enterFullscreen) + { + // NOTE: The setTimeouts handle the browser mode change delay + EM_ASM + ( + setTimeout(function() + { + Module.requestFullscreen(false, false); + }, 100); + ); + CORE.Window.fullscreen = true; + FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); + } + + // NOTE: Old notes below: + /* + EM_ASM + ( + // This strategy works well while using raylib minimal web shell for emscripten, + // it re-scales the canvas to fullscreen using monitor resolution, for tools this + // is a good strategy but maybe games prefer to keep current canvas resolution and + // display it in fullscreen, adjusting monitor resolution if possible + if (document.fullscreenElement) document.exitFullscreen(); + else Module.requestFullscreen(true, true); //false, true); + ); + */ + // EM_ASM(Module.requestFullscreen(false, false);); + /* + if (!CORE.Window.fullscreen) + { + // Option 1: Request fullscreen for the canvas element + // This option does not seem to work at all: + // emscripten_request_pointerlock() and emscripten_request_fullscreen() are affected by web security, + // the user must click once on the canvas to hide the pointer or transition to full screen + //emscripten_request_fullscreen("#canvas", false); + + // Option 2: Request fullscreen for the canvas element with strategy + // This option does not seem to work at all + // REF: https://github.com/emscripten-core/emscripten/issues/5124 + // EmscriptenFullscreenStrategy strategy = { + // .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, //EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, + // .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF, + // .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT, + // .canvasResizedCallback = EmscriptenWindowResizedCallback, + // .canvasResizedCallbackUserData = NULL + // }; + //emscripten_request_fullscreen_strategy("#canvas", EM_FALSE, &strategy); + + // Option 3: Request fullscreen for the canvas element with strategy + // It works as expected but only inside the browser (client area) + EmscriptenFullscreenStrategy strategy = { + .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, + .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF, + .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT, + .canvasResizedCallback = EmscriptenWindowResizedCallback, + .canvasResizedCallbackUserData = NULL + }; + emscripten_enter_soft_fullscreen("#canvas", &strategy); + + int width = 0; + int height = 0; + emscripten_get_canvas_element_size("#canvas", &width, &height); + TRACELOG(LOG_WARNING, "Emscripten: Enter fullscreen: Canvas size: %i x %i", width, height); + + CORE.Window.fullscreen = true; // Toggle fullscreen flag + FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); + } + else + { + //emscripten_exit_fullscreen(); + //emscripten_exit_soft_fullscreen(); + + int width, height; + emscripten_get_canvas_element_size("#canvas", &width, &height); + TRACELOG(LOG_WARNING, "Emscripten: Exit fullscreen: Canvas size: %i x %i", width, height); + + CORE.Window.fullscreen = false; // Toggle fullscreen flag + FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); + } + */ +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + bool enterBorderless = false; + + const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0); + if (wasFullscreen) + { + if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) enterBorderless = false; + else if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) enterBorderless = true; + else + { + const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0); + const int screenWidth = EM_ASM_INT( { return screen.width; }, 0); + if (screenWidth == canvasWidth) enterBorderless = false; + else enterBorderless = true; + } + + EM_ASM(document.exitFullscreen();); + + CORE.Window.fullscreen = false; + FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); + FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE); + } + else enterBorderless = true; + + if (enterBorderless) + { + // 1. The setTimeouts handle the browser mode change delay + // 2. The style unset handles the possibility of a width="value%" like on the default shell.html file + EM_ASM + ( + setTimeout(function() + { + Module.requestFullscreen(false, true); + setTimeout(function() + { + canvas.style.width="unset"; + }, 100); + }, 100); + ); + FLAG_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE); + } +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) && !FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) + { + const int tabWidth = EM_ASM_INT( return window.innerWidth; ); + const int tabHeight = EM_ASM_INT( return window.innerHeight; ); + + FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); + } +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform"); +} + +// Restore window from being minimized/maximized +void RestoreWindow(void) +{ + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) && FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) + { + FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); + } +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead"); + + // Check previous state and requested state to apply required changes + // NOTE: In most cases the functions already change the flags internally + + // State change: FLAG_VSYNC_HINT + if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT)) + { + TRACELOG(LOG_WARNING, "SetWindowState(FLAG_VSYNC_HINT) not available on target platform"); + } + + // State change: FLAG_BORDERLESS_WINDOWED_MODE + if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE)) + { + // NOTE: Window state flag updated inside ToggleBorderlessWindowed() function + const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0); + if (wasFullscreen) + { + const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0); + const int canvasStyleWidth = EM_ASM_INT( { return parseInt(Module.canvas.style.width); }, 0); + if ((FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) || canvasStyleWidth > canvasWidth) ToggleBorderlessWindowed(); + } + else ToggleBorderlessWindowed(); + } + + // State change: FLAG_FULLSCREEN_MODE + if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE)) + { + // NOTE: Window state flag updated inside ToggleFullscreen() function + const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0); + if (wasFullscreen) + { + const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0); + const int screenWidth = EM_ASM_INT( { return screen.width; }, 0); + if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE) || (screenWidth == canvasWidth)) ToggleFullscreen(); + } + else ToggleFullscreen(); + } + + // State change: FLAG_WINDOW_RESIZABLE + if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)) && FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)) + { + FLAG_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE); + } + + // State change: FLAG_WINDOW_UNDECORATED + if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED)) + { + TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_UNDECORATED) not available on target platform"); + } + + // State change: FLAG_WINDOW_HIDDEN + if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN)) + { + TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_HIDDEN) not available on target platform"); + } + + // State change: FLAG_WINDOW_MINIMIZED + if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED)) + { + TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_MINIMIZED) not available on target platform"); + } + + // State change: FLAG_WINDOW_MAXIMIZED + if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED) != FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED)) && FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED)) + { + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) + { + const int tabWidth = EM_ASM_INT( return window.innerWidth; ); + const int tabHeight = EM_ASM_INT( return window.innerHeight; ); + + FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); + } + } + + // State change: FLAG_WINDOW_UNFOCUSED + if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED)) + { + TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_UNFOCUSED) not available on target platform"); + } + + // State change: FLAG_WINDOW_TOPMOST + if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST)) + { + TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_TOPMOST) not available on target platform"); + } + + // State change: FLAG_WINDOW_ALWAYS_RUN + if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN)) + { + TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_ALWAYS_RUN) not available on target platform"); + } + + // The following states can not be changed after window creation + // NOTE: Review for PLATFORM_WEB + + // State change: FLAG_WINDOW_TRANSPARENT + if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT)) + { + TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_TRANSPARENT) not available on target platform"); + } + + // State change: FLAG_WINDOW_HIGHDPI + if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI)) + { + TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_HIGHDPI) not available on target platform"); + } + + // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH + if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH)) + { + TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_MOUSE_PASSTHROUGH) not available on target platform"); + } + + // State change: FLAG_MSAA_4X_HINT + if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT)) + { + TRACELOG(LOG_WARNING, "SetWindowState(FLAG_MSAA_4X_HINT) not available on target platform"); + } + + // State change: FLAG_INTERLACED_HINT + if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT)) + { + TRACELOG(LOG_WARNING, "SetWindowState(FLAG_INTERLACED_HINT) not available on target platform"); + } +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + // Check previous state and requested state to apply required changes + // NOTE: In most cases the functions already change the flags internally + + // State change: FLAG_VSYNC_HINT + if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT)) + { + TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_VSYNC_HINT) not available on target platform"); + } + + // State change: FLAG_BORDERLESS_WINDOWED_MODE + if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE)) + { + const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0); + if (wasFullscreen) + { + const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0); + const int screenWidth = EM_ASM_INT( { return screen.width; }, 0); + if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE) || (screenWidth == canvasWidth)) EM_ASM(document.exitFullscreen();); + } + + FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE); + } + + // State change: FLAG_FULLSCREEN_MODE + if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE)) + { + const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0); + if (wasFullscreen) + { + const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0); + const int canvasStyleWidth = EM_ASM_INT( { return parseInt(Module.canvas.style.width); }, 0); + if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE) || (canvasStyleWidth > canvasWidth)) EM_ASM(document.exitFullscreen();); + } + + CORE.Window.fullscreen = false; + FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); + } + + // State change: FLAG_WINDOW_RESIZABLE + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) && FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)) + { + FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_RESIZABLE); + } + + // State change: FLAG_WINDOW_HIDDEN + if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN)) + { + TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_HIDDEN) not available on target platform"); + } + + // State change: FLAG_WINDOW_MINIMIZED + if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED)) + { + TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_MINIMIZED) not available on target platform"); + } + + // State change: FLAG_WINDOW_MAXIMIZED + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED) && FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED)) + { + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) + { + FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); + } + } + + // State change: FLAG_WINDOW_UNDECORATED + if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED)) + { + TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_UNDECORATED) not available on target platform"); + } + + // State change: FLAG_WINDOW_UNFOCUSED + if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED)) + { + TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_UNFOCUSED) not available on target platform"); + } + + // State change: FLAG_WINDOW_TOPMOST + if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST)) + { + TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_TOPMOST) not available on target platform"); + } + + // State change: FLAG_WINDOW_ALWAYS_RUN + if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN)) + { + TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_ALWAYS_RUN) not available on target platform"); + } + + // The following states can not be changed after window creation + // NOTE: Review for PLATFORM_WEB + + // State change: FLAG_WINDOW_TRANSPARENT + if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT)) + { + TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_TRANSPARENT) not available on target platform"); + } + + // State change: FLAG_WINDOW_HIGHDPI + if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI)) + { + TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_HIGHDPI) not available on target platform"); + } + + // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH + if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH)) + { + TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_MOUSE_PASSTHROUGH) not available on target platform"); + } + + // State change: FLAG_MSAA_4X_HINT + if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT)) + { + TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_MSAA_4X_HINT) not available on target platform"); + } + + // State change: FLAG_INTERLACED_HINT + if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT)) + { + TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_INTERLACED_HINT) not available on target platform"); + } +} + +// Set icon for window +void SetWindowIcon(Image image) +{ + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform"); +} + +// Set icon for window, multiple images +void SetWindowIcons(Image *images, int count) +{ + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); +} + +// Set title for window +void SetWindowTitle(const char *title) +{ + CORE.Window.title = title; + emscripten_set_window_title(title); +} + +// Set window position on screen (windowed mode) +void SetWindowPosition(int x, int y) +{ + TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform"); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + CORE.Window.screenMin.width = width; + CORE.Window.screenMin.height = height; + + // Trigger the resize event once to update the window minimum width and height + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; + + // Trigger the resize event once to update the window maximum width and height + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + // When resizing the canvas, several elements must be considered: + // - CSS canvas size: Web layout size, logical pixels + // - Canvas contained framebuffer resolution + // * Browser monitor, device pixel ratio (HighDPI) + + double canvasCssWidth = 0.0; + double canvasCssHeight = 0.0; + emscripten_get_element_css_size(platform.canvasId, &canvasCssWidth, &canvasCssHeight); + + // NOTE: emscripten_get_canvas_element_size() returns canvas framebuffer size, not CSS canvas size + + // Get device pixel ratio + // TODO: Should DPI be considered at this point? + double dpr = emscripten_get_device_pixel_ratio(); + + // Set canvas framebuffer size + emscripten_set_canvas_element_size(platform.canvasId, width*dpr, height*dpr); + + // Set canvas CSS size + // TODO: Consider canvas CSS style if already scaled 100% + EM_ASM({ Module.canvas.style.width = $0; }, width*dpr); + EM_ASM({ Module.canvas.style.height = $0; }, height*dpr); + + SetupViewport(width*dpr, height*dpr); // Reset viewport and projection matrix for new size +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + if (opacity >= 1.0f) opacity = 1.0f; + else if (opacity <= 0.0f) opacity = 0.0f; + + EM_ASM({ Module.canvas.style.opacity = $0; }, opacity); +} + +// Set window focused +void SetWindowFocused(void) +{ + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); +} + +// Get native window handle +void *GetWindowHandle(void) +{ + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform"); + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform"); + return 1; +} + +// Get current monitor where window is placed +int GetCurrentMonitor(void) +{ + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform"); + return 0; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + // Get the width of the user's entire screen in CSS logical pixels, + // no physical pixels, it would require multiplying by device pixel ratio + // NOTE: Returned value is limited to the current monitor where the browser window is located + int width = 0; + width = EM_ASM_INT( { return window.screen.width; }, 0); + return width; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + // Get the height of the user's entire screen in CSS logical pixels, + // no physical pixels, it would require multiplying by device pixel ratio + // NOTE: Returned value is limited to the current monitor where the browser window is located + int height = 0; + height = EM_ASM_INT( { return window.screen.height; }, 0); + return height; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform"); + return 0; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform"); + return 0; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform"); + return 0; +} + +// Get the human-readable, UTF-8 encoded name of the selected monitor +const char *GetMonitorName(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform"); + return ""; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + // Browser window position, top-left corner relative to the physical screen origin, expressed in CSS logical pixels + // NOTE: Returned position is relative to the current monitor where the browser window is located + Vector2 position = { 0, 0 }; + position.x = (float)EM_ASM_INT( { return window.screenX; }, 0); + position.y = (float)EM_ASM_INT( { return window.screenY; }, 0); + return position; +} + +// Get current monitor device pixel ratio +Vector2 GetWindowScaleDPI(void) +{ + // Get device pixel ratio + // NOTE: Returned scale is relative to the current monitor where the browser window is located + Vector2 scale = { 1.0f, 1.0f }; + scale.x = (float)EM_ASM_DOUBLE( { return window.devicePixelRatio; } ); + scale.y = scale.x; + return scale; +} + +// Set clipboard text content +void SetClipboardText(const char *text) +{ + // Security check to (partially) avoid malicious code + if (strchr(text, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided Clipboard could be potentially malicious, avoid [\'] character"); + else EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ +/* + // Accessing clipboard data from browser is tricky due to security reasons + // The method to use is navigator.clipboard.readText() but this is an asynchronous method + // that will return at some moment after the function is called with the required data + emscripten_run_script_string("navigator.clipboard.readText() \ + .then(text => { document.getElementById('clipboard').innerText = text; console.log('Pasted content: ', text); }) \ + .catch(err => { console.error('Failed to read clipboard contents: ', err); });" + ); + + // The main issue is getting that data, one approach could be using ASYNCIFY and wait + // for the data but it requires adding Asyncify emscripten library on compilation + + // Another approach could be just copy the data in a HTML text field and try to retrieve it + // later on if available... and clean it for future accesses +*/ + return NULL; +} + +// Get clipboard image +Image GetClipboardImage(void) +{ + Image image = { 0 }; + + // NOTE: In theory, the new navigator.clipboard.read() can be used to return arbitrary data from clipboard (2024) + // REF: https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/read + TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform"); + + return image; +} + +// Show mouse cursor +void ShowCursor(void) +{ + if (CORE.Input.Mouse.cursorHidden) + { + EM_ASM( { Module.canvas.style.cursor = UTF8ToString($0); }, cursorLUT[CORE.Input.Mouse.cursor]); + + CORE.Input.Mouse.cursorHidden = false; + } +} + +// Hides mouse cursor +void HideCursor(void) +{ + if (!CORE.Input.Mouse.cursorHidden) + { + EM_ASM(Module.canvas.style.cursor = 'none';); + + CORE.Input.Mouse.cursorHidden = true; + } +} + +// Enables cursor (unlock cursor) +void EnableCursor(void) +{ + emscripten_exit_pointerlock(); + + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + // NOTE: CORE.Input.Mouse.cursorLocked handled by EmscriptenPointerlockCallback() +} + +// Disables cursor (lock cursor) +void DisableCursor(void) +{ + emscripten_request_pointerlock(platform.canvasId, 1); + + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + // NOTE: CORE.Input.Mouse.cursorLocked handled by EmscriptenPointerlockCallback() +} + +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ +#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + // Update framebuffer + rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, platform.pixels); + + // Copy framebuffer data into canvas + EM_ASM({ + const width = $0; + const height = $1; + const ptr = $2; + + // Get canvas and 2d context created + const canvas = Module.canvas; + //const canvas = Module['canvas']; + const ctx = canvas.getContext('2d'); + + if (!Module.__img || (Module.__img.width !== width) || (Module.__img.height !== height)) { + Module.__img = ctx.createImageData(width, height); + } + + const src = HEAPU8.subarray(ptr, ptr + width*height*4); // RGBA (4 bytes) + Module.__img.data.set(src); + ctx.putImageData(Module.__img, 0, 0); + + }, CORE.Window.screen.width, CORE.Window.screen.height, platform.pixels); +#endif +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + +// Get elapsed time measure in seconds since InitTimer() +double GetTime(void) +{ + double time = 0.0; + /* + struct timespec ts = { 0 }; + clock_gettime(CLOCK_MONOTONIC, &ts); + unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; + time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() + */ + time = emscripten_get_now()*1000.0; + + return time; +} + +// Open URL with default system browser (if available) +// NOTE: This function is only safe to use if you control the URL given +// A user could craft a malicious string performing another action +// Only call this function yourself not with user input or make sure to check the string yourself +void OpenURL(const char *url) +{ + // Security check to (partially) avoid malicious code on target platform + if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); + else emscripten_run_script(TextFormat("window.open('%s', '_blank')", url)); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + TRACELOG(LOG_INFO, "SetGamepadMappings not implemented in rcore_web.c"); + + return 0; +} + +// Set gamepad vibration +void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration) +{ + if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (duration > 0.0f)) + { + if (leftMotor < 0.0f) leftMotor = 0.0f; + if (leftMotor > 1.0f) leftMotor = 1.0f; + if (rightMotor < 0.0f) rightMotor = 0.0f; + if (rightMotor > 1.0f) rightMotor = 1.0f; + if (duration > MAX_GAMEPAD_VIBRATION_TIME) duration = MAX_GAMEPAD_VIBRATION_TIME; + duration *= 1000.0f; // Convert duration to ms + + // NOTE: [2024.10.21] Current browser support: + // - vibrationActuator API: Chrome, Edge, Opera, Safari, Android Chrome, Android Webview + // - hapticActuators API: Firefox + EM_ASM({ + try { navigator.getGamepads()[$0].vibrationActuator.playEffect('dual-rumble', { startDelay: 0, duration: $3, weakMagnitude: $1, strongMagnitude: $2 }); } + catch (e) + { + try { navigator.getGamepads()[$0].hapticActuators[0].pulse($2, $3); } + catch (e) { } + } + }, gamepad, leftMotor, rightMotor, duration); + } +} + +// Set mouse position XY +void SetMousePosition(int x, int y) +{ + // WARNING: Not supported by browser for security reasons +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + if (CORE.Input.Mouse.cursor != cursor) + { + if (!CORE.Input.Mouse.cursorLocked) EM_ASM( { Module.canvas.style.cursor = UTF8ToString($0); }, cursorLUT[cursor]); + CORE.Input.Mouse.cursor = cursor; + } +} + +// Get physical key name +const char *GetKeyName(int key) +{ + // TODO: Browser can definitely provide a key name e->key + TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform"); + return ""; +} + +// Register all input events +void PollInputEvents(void) +{ +#if defined(SUPPORT_GESTURES_SYSTEM) + // NOTE: Gestures update must be called every frame to reset gestures correctly + // because ProcessGestureEvent() is just called on an event, not every frame + UpdateGestures(); +#endif + + // Reset keys/chars pressed registered + CORE.Input.Keyboard.keyPressedQueueCount = 0; + CORE.Input.Keyboard.charPressedQueueCount = 0; + + // Reset last gamepad button/axis registered state + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + //CORE.Input.Gamepad.axisCount = 0; + + // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback) + + // Register previous keys states + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // Register previous mouse states + for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; + + // Register previous mouse wheel state + CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; + CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; + + // Register previous mouse position + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + + // Register previous touch states + for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; + + // Reset touch positions + // TODO: It resets on target platform the mouse position and not filled again until a move-event, + // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! + //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + + // Get number of gamepads connected + int numGamepads = 0; + if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS) numGamepads = emscripten_get_num_gamepads(); + + for (int i = 0; (i < numGamepads) && (i < MAX_GAMEPADS); i++) + { + // Register previous gamepad button states + for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; + + EmscriptenGamepadEvent gamepadState = { 0 }; + int result = emscripten_get_gamepad_status(i, &gamepadState); + + if (result == EMSCRIPTEN_RESULT_SUCCESS) + { + // Register buttons data for every connected gamepad + for (int j = 0; (j < gamepadState.numButtons) && (j < MAX_GAMEPAD_BUTTONS); j++) + { + GamepadButton button = -1; + + // Gamepad Buttons reference: https://www.w3.org/TR/gamepad/#gamepad-interface + switch (j) + { + case 0: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; + case 1: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; + case 2: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + case 3: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; + case 4: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; + case 5: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; + case 6: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break; + case 7: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break; + case 8: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; + case 9: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; + case 10: button = GAMEPAD_BUTTON_LEFT_THUMB; break; + case 11: button = GAMEPAD_BUTTON_RIGHT_THUMB; break; + case 12: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; + case 13: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; + case 14: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; + case 15: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; + default: break; + } + + if (button + 1 != 0) // Check for valid button + { + if (gamepadState.digitalButton[j] == 1) + { + CORE.Input.Gamepad.currentButtonState[i][button] = 1; + CORE.Input.Gamepad.lastButtonPressed = button; + } + else CORE.Input.Gamepad.currentButtonState[i][button] = 0; + } + + //TRACELOGD("INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]); + } + + // Register axis data for every connected gamepad + for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXES); j++) + { + CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j]; + } + + CORE.Input.Gamepad.axisCount[i] = gamepadState.numAxes; + } + } + + CORE.Window.resizedLastFrame = false; +} + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize platform: graphics, inputs and more +int InitPlatform(void) +{ + SetCanvasIdJs(platform.canvasId, 64); // Get the current canvas id + + // Initialize graphic device: display/window and graphic context + //---------------------------------------------------------------------------- + emscripten_set_canvas_element_size(platform.canvasId, CORE.Window.screen.width, CORE.Window.screen.height); + EmscriptenWebGLContextAttributes attribs = { 0 }; + emscripten_webgl_init_context_attributes(&attribs); + attribs.alpha = EM_TRUE; + attribs.depth = EM_TRUE; + attribs.stencil = EM_FALSE; + attribs.antialias = EM_FALSE; + + // Check window creation flags + //if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) CORE.Window.fullscreen = true; + + // Disable FLAG_WINDOW_MINIMIZED, not supported + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); + + // Disable FLAG_WINDOW_MAXIMIZED, not supported + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); + + // Disable FLAG_WINDOW_TOPMOST, not supported + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TOPMOST)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_TOPMOST); + + // NOTE: Some other flags are not supported on HTML5 + + // TODO: Scale content area based on the monitor content scale where window is placed on + + // Request MSAA (usually x4 on WebGL 1.0) + if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) attribs.antialias = EM_TRUE; + + // Check selection OpenGL version + if (rlGetVersion() == RL_OPENGL_11_SOFTWARE) + { + // Avoid creating a WebGL canvas, create 2d canvas for software rendering + emscripten_set_canvas_element_size(platform.canvasId, CORE.Window.screen.width, CORE.Window.screen.height); + EM_ASM({ + const canvas = document.getElementById(platform.canvasId); + Module.canvas = canvas; + }); + + // Load memory framebuffer with desired screen size + platform.pixels = (unsigned int *)RL_CALLOC(CORE.Window.screen.width*CORE.Window.screen.height, sizeof(unsigned int)); + } + else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context --> WebGL 1.0 + { + attribs.majorVersion = 1; // WebGL 1.0 requested + attribs.minorVersion = 0; + + // Create WebGL context + platform.glContext = emscripten_webgl_create_context(platform.canvasId, &attribs); + if (platform.glContext == 0) return 0; + + emscripten_webgl_make_context_current(platform.glContext); + } + else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context --> WebGL 2.0 + { + attribs.majorVersion = 2; // WebGL 2.0 requested + attribs.minorVersion = 0; + + // Create WebGL context + platform.glContext = emscripten_webgl_create_context(platform.canvasId, &attribs); + if (platform.glContext == 0) return 0; + + emscripten_webgl_make_context_current(platform.glContext); + } + + // NOTE: Getting video modes is not implemented in emscripten GLFW3 version + CORE.Window.display.width = CORE.Window.screen.width; + CORE.Window.display.height = CORE.Window.screen.height; + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + + // Set default window title + emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " "); + + // Check context activation + if ((platform.glContext != 0) || (platform.pixels != NULL)) + { + CORE.Window.ready = true; + + int fbWidth = CORE.Window.screen.width; + int fbHeight = CORE.Window.screen.height; + + CORE.Window.render.width = fbWidth; + CORE.Window.render.height = fbHeight; + CORE.Window.currentFbo.width = fbWidth; + CORE.Window.currentFbo.height = fbHeight; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + } + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + return -1; + } + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + if (platform.glContext != 0) rlLoadExtensions(emscripten_webgl_get_proc_address); + //---------------------------------------------------------------------------- + + // Initialize events callbacks + //---------------------------------------------------------------------------- + // Setup window/canvas events callbacks + emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenFullscreenChangeCallback); + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); + emscripten_set_blur_callback(platform.canvasId, NULL, 1, EmscriptenFocusCallback); + emscripten_set_focus_callback(platform.canvasId, NULL, 1, EmscriptenFocusCallback); + emscripten_set_visibilitychange_callback(NULL, 1, EmscriptenVisibilityChangeCallback); + + // Setup input events + emscripten_set_keypress_callback(platform.canvasId, NULL, 1, EmscriptenKeyboardCallback); + emscripten_set_keydown_callback(platform.canvasId, NULL, 1, EmscriptenKeyboardCallback); + emscripten_set_keyup_callback(platform.canvasId, NULL, 1, EmscriptenKeyboardCallback); + + emscripten_set_click_callback(platform.canvasId, NULL, 1, EmscriptenMouseCallback); + //emscripten_set_dblclick_callback(platform.canvasId, NULL, 1, EmscriptenMouseCallback); + emscripten_set_mousedown_callback(platform.canvasId, NULL, 1, EmscriptenMouseCallback); + emscripten_set_mouseup_callback(platform.canvasId, NULL, 1, EmscriptenMouseCallback); + emscripten_set_mousemove_callback(platform.canvasId, NULL, 1, EmscriptenMouseCallback); + emscripten_set_mousemove_callback(platform.canvasId, NULL, 1, EmscriptenMouseMoveCallback); + emscripten_set_wheel_callback(platform.canvasId, NULL, 1, EmscriptenMouseWheelCallback); + emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenPointerlockCallback); + + emscripten_set_touchstart_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchend_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchmove_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchcancel_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback); + + emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); + emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + + // Trigger resize callback to force initial size + EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); + //---------------------------------------------------------------------------- + + // Initialize timing system + //---------------------------------------------------------------------------- + InitTimer(); + //---------------------------------------------------------------------------- + + // Initialize storage system + //---------------------------------------------------------------------------- + CORE.Storage.basePath = GetWorkingDirectory(); + //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: WEB: Initialized successfully"); + + return 0; +} + +// Close platform +// NOTE: Platform closing is managed by browser, so, +// this function is actually not required, but still +// implementing some logic behaviour +void ClosePlatform(void) +{ + if (platform.pixels != NULL) RL_FREE(platform.pixels); + if (platform.glContext != 0) emscripten_webgl_destroy_context(platform.glContext); +} + +// Emscripten callback functions, called on specific browser events +//------------------------------------------------------------------------------------------------------- +// Emscripten: Called on resize event +static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData) +{ + // Don't resize non-resizeable windows + if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) return 1; +/* + // Set current screen size + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) + { + Vector2 windowScaleDPI = GetWindowScaleDPI(); + + CORE.Window.screen.width = (unsigned int)(width/windowScaleDPI.x); + CORE.Window.screen.height = (unsigned int)(height/windowScaleDPI.y); + } + else + { + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + } +*/ + // This event is called whenever the window changes sizes, + // so the size of the canvas object is explicitly retrieved below + int width = EM_ASM_INT( return window.innerWidth; ); + int height = EM_ASM_INT( return window.innerHeight; ); + + if (width < (int)CORE.Window.screenMin.width) width = CORE.Window.screenMin.width; + else if ((width > (int)CORE.Window.screenMax.width) && (CORE.Window.screenMax.width > 0)) width = CORE.Window.screenMax.width; + + if (height < (int)CORE.Window.screenMin.height) height = CORE.Window.screenMin.height; + else if ((height > (int)CORE.Window.screenMax.height) && (CORE.Window.screenMax.height > 0)) height = CORE.Window.screenMax.height; + + emscripten_set_canvas_element_size(platform.canvasId, width, height); + + SetupViewport(width, height); // Reset viewport and projection matrix for new size + + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + CORE.Window.resizedLastFrame = true; + + if (IsWindowFullscreen()) return 1; + + // Set current screen size + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + + // NOTE: Postprocessing texture is not scaled to new size + + return 0; +} + +// Emscripten: Called on windows focus change events +static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData) +{ + EM_BOOL consumed = 1; + + switch (eventType) + { + case EMSCRIPTEN_EVENT_BLUR: FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break; // The canvas lost focus + case EMSCRIPTEN_EVENT_FOCUS: FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break; + default: consumed = 0; break; + } + + return consumed; +} + +// Emscripten: Called on visibility change events +static EM_BOOL EmscriptenVisibilityChangeCallback(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData) +{ + if (visibilityChangeEvent->hidden) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN); // The window was hidden + else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_HIDDEN); // The window was restored + + return 1; // The event was consumed by the callback handler +} + +// Emscripten: Called on fullscreen change events +// TODO: Review fullscreen strategy +static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData) +{ + // NOTE: Reset the fullscreen flags if the user left fullscreen manually by pressing the Escape key + const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0); + if (!wasFullscreen) + { + CORE.Window.fullscreen = false; + FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); + FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE); + } + + return 1; // The event was consumed by the callback handler +} + +/* +// GLFW3: Called on file-drop over the window +// TODO: Implement Emscripten (or HTML5/JS) alternative +static void WindowDropCallback(GLFWwindow *window, int count, const char **paths) +{ + if (count > 0) + { + // In case previous dropped filepaths have not been freed, we free them + if (CORE.Window.dropFileCount > 0) + { + 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 *)); + + for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) + { + CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); + strcpy(CORE.Window.dropFilepaths[i], paths[i]); + } + } +} +*/ + +// Emscripten: Called on key events +// TODO: keyCodes should be mapped to raylib/GLFW3 Key values +static EM_BOOL EmscriptenKeyboardCallback(int eventType, const EmscriptenKeyboardEvent *keyboardEvent, void *userData) +{ + switch (eventType) + { + case EMSCRIPTEN_EVENT_KEYPRESS: + { + if (keyboardEvent->repeat) CORE.Input.Keyboard.keyRepeatInFrame[keyboardEvent->keyCode] = 1; + } break; + case EMSCRIPTEN_EVENT_KEYDOWN: + { + CORE.Input.Keyboard.currentKeyState[keyboardEvent->keyCode] = 1; + } break; + case EMSCRIPTEN_EVENT_KEYUP: + { + CORE.Input.Keyboard.currentKeyState[keyboardEvent->keyCode] = 0; + } break; + default: break; + } + + // TODO: Add char codes + //unsigned int charCode + // Check if there is space available in the queue for characters to be added + /* + if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) + { + // Add character to the queue + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = keyboardEvent->charCode; + CORE.Input.Keyboard.charPressedQueueCount++; + } + */ + /* + // Check if there is space available in the key queue + if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (eventType == EMSCRIPTEN_EVENT_KEYPRESS)) + { + // Add character to the queue + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keyboardEvent->keyCode; + CORE.Input.Keyboard.keyPressedQueueCount++; + } + + // Check the exit key to set close window + //if ((keyboardEvent->keyCode == CORE.Input.Keyboard.exitKey) && (eventType == EMSCRIPTEN_EVENT_KEYPRESS)) CORE.Window.shouldClose = true; + */ + + return 1; // The event was consumed by the callback handler +} + +// Emscripten: Called on mouse input events +static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) +{ + switch (eventType) + { + case EMSCRIPTEN_EVENT_MOUSEENTER: CORE.Input.Mouse.cursorOnScreen = true; break; + case EMSCRIPTEN_EVENT_MOUSELEAVE: CORE.Input.Mouse.cursorOnScreen = false; break; + case EMSCRIPTEN_EVENT_MOUSEDOWN: + { + // NOTE: Emscripten and raylib buttons indices are not aligned + if (mouseEvent->button == 0) CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT] = 1; + else if (mouseEvent->button == 1) CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_MIDDLE] = 1; + else if (mouseEvent->button == 2) CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_RIGHT] = 1; + + //CORE.Input.Touch.currentTouchState[button] = action; + } break; + case EMSCRIPTEN_EVENT_MOUSEUP: + { + if (mouseEvent->button == 0) CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT] = 0; + else if (mouseEvent->button == 1) CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_MIDDLE] = 0; + else if (mouseEvent->button == 2) CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_RIGHT] = 0; + } break; + default: break; + } + +#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) + // Process mouse events as touches to be able to use mouse-gestures + GestureEvent gestureEvent = { 0 }; + + // Register touch actions + if ((CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT] == 1) && (CORE.Input.Mouse.previousButtonState[MOUSE_BUTTON_LEFT] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN; + else if ((CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT] == 0) && (CORE.Input.Mouse.previousButtonState[MOUSE_BUTTON_LEFT] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP; + + // NOTE: TOUCH_ACTION_MOVE event is registered in MouseMoveCallback() + + // Assign a pointer ID + gestureEvent.pointId[0] = 0; + + // Register touch points count + gestureEvent.pointCount = 1; + + // Register touch points position, only one point registered + gestureEvent.position[0] = GetMousePosition(); + + // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + + // Gesture data is sent to gestures-system for processing + // Prevent calling ProcessGestureEvent() when Emscripten is present and there's a touch gesture, so EmscriptenTouchCallback() can handle it itself + if (GetMouseX() != 0 || GetMouseY() != 0) ProcessGestureEvent(gestureEvent); +#endif + + return 1; // The event was consumed by the callback handler +} + +// Emscripten: Called on mouse move events +static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) +{ + if (CORE.Input.Mouse.cursorLocked) + { + CORE.Input.Mouse.previousPosition.x = CORE.Input.Mouse.lockedPosition.x - mouseEvent->movementX; + CORE.Input.Mouse.previousPosition.y = CORE.Input.Mouse.lockedPosition.y - mouseEvent->movementY; + } + else + { + // Get mouse position in canvas CSS pixels + float mouseCssX = (float)mouseEvent->canvasX; + float mouseCssY = (float)mouseEvent->canvasY; + + // Get canvas sizes + double cssWidth = 0.0; + double cssHeight = 0.0; + emscripten_get_element_css_size(platform.canvasId, &cssWidth, &cssHeight); + + int fbWidth = 0; + int fbHeight = 0; + emscripten_get_canvas_element_size(platform.canvasId, &fbWidth, &fbHeight); + + // Convert CSS to framebuffer coordinates + float scaleX = (float)fbWidth/(float)cssWidth; + float scaleY = (float)fbHeight/(float)cssHeight; + + int mouseX = (int)(mouseCssX*scaleX); + int mouseY = (int)(mouseCssY*scaleY); + + CORE.Input.Mouse.currentPosition.x = mouseX;//(float)mouseEvent->canvasX; + CORE.Input.Mouse.currentPosition.y = mouseY;//(float)mouseEvent->canvasY; + + // Shorter alternative: + //double dpr = emscripten_get_device_pixel_ratio(); + //int mouseX = (int)(e->canvasX*dpr); + //int mouseY = (int)(e->canvasY*dpr); + + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; + } + +#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) + // Process mouse events as touches to be able to use mouse-gestures + GestureEvent gestureEvent = { 0 }; + + gestureEvent.touchAction = TOUCH_ACTION_MOVE; + + // Assign a pointer ID + gestureEvent.pointId[0] = 0; + + // Register touch points count + gestureEvent.pointCount = 1; + + // Register touch points position, only one point registered + gestureEvent.position[0] = CORE.Input.Touch.position[0]; + + // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + + // Gesture data is sent to gestures-system for processing + ProcessGestureEvent(gestureEvent); +#endif + + return 1; // The event was consumed by the callback handler +} + +// Emscripten: Called on mouse wheel events +static EM_BOOL EmscriptenMouseWheelCallback(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData) +{ + if (eventType == EMSCRIPTEN_EVENT_WHEEL) + { + CORE.Input.Mouse.currentWheelMove.x = (float)wheelEvent->deltaX; + CORE.Input.Mouse.currentWheelMove.y = (float)wheelEvent->deltaY; + } + + return 1; // The event was consumed by the callback handler +} + +// Emscripten: Called on pointer lock events +static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData) +{ + CORE.Input.Mouse.cursorLocked = EM_ASM_INT( { if (document.pointerLockElement) return 1; }, 0); + + if (CORE.Input.Mouse.cursorLocked) + { + CORE.Input.Mouse.lockedPosition = CORE.Input.Mouse.currentPosition; + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.lockedPosition; + } + + return 1; // The event was consumed by the callback handler +} + +// Emscripten: Called on connect/disconnect gamepads events +static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) +{ + /* + TRACELOGD("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"", + eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state", + gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping); + + for (int i = 0; i < gamepadEvent->numAxes; i++) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]); + for (int i = 0; i < gamepadEvent->numButtons; i++) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); + */ + + if (gamepadEvent->connected && (gamepadEvent->index < MAX_GAMEPADS)) + { + CORE.Input.Gamepad.ready[gamepadEvent->index] = true; + snprintf(CORE.Input.Gamepad.name[gamepadEvent->index], MAX_GAMEPAD_NAME_LENGTH, "%s", gamepadEvent->id); + } + else CORE.Input.Gamepad.ready[gamepadEvent->index] = false; + + return 1; // The event was consumed by the callback handler +} + +// Emscripten: Called on touch input events +static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) +{ + // Register touch points count + CORE.Input.Touch.pointCount = touchEvent->numTouches; + + double canvasWidth = 0.0; + double canvasHeight = 0.0; + // NOTE: emscripten_get_canvas_element_size() returns canvas.width and canvas.height but + // we are looking for actual CSS size: canvas.style.width and canvas.style.height + // EMSCRIPTEN_RESULT res = emscripten_get_canvas_element_size("#canvas", &canvasWidth, &canvasHeight); + emscripten_get_element_css_size(platform.canvasId, &canvasWidth, &canvasHeight); + + for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++) + { + // Register touch points id + CORE.Input.Touch.pointId[i] = touchEvent->touches[i].identifier; + + // Register touch points position + CORE.Input.Touch.position[i] = (Vector2){touchEvent->touches[i].targetX, touchEvent->touches[i].targetY}; + + // Normalize gestureEvent.position[x] for CORE.Window.screen.width and CORE.Window.screen.height + CORE.Input.Touch.position[i].x *= ((float)GetScreenWidth()/(float)canvasWidth); + CORE.Input.Touch.position[i].y *= ((float)GetScreenHeight()/(float)canvasHeight); + + if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) CORE.Input.Touch.currentTouchState[i] = 1; + else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0; + } + + // Update mouse position if we detect a single touch + if (CORE.Input.Touch.pointCount == 1) + { + CORE.Input.Mouse.currentPosition.x = CORE.Input.Touch.position[0].x; + CORE.Input.Mouse.currentPosition.y = CORE.Input.Touch.position[0].y; + } + +#if defined(SUPPORT_GESTURES_SYSTEM) + GestureEvent gestureEvent = { 0 }; + gestureEvent.pointCount = CORE.Input.Touch.pointCount; + + // Register touch actions + if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) gestureEvent.touchAction = TOUCH_ACTION_DOWN; + else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) gestureEvent.touchAction = TOUCH_ACTION_UP; + else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE; + else if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL; + + for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++) + { + gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i]; + gestureEvent.position[i] = CORE.Input.Touch.position[i]; + + // Normalize gestureEvent.position[i] + gestureEvent.position[i].x /= (float)GetScreenWidth(); + gestureEvent.position[i].y /= (float)GetScreenHeight(); + } + + // Gesture data is sent to gestures system for processing + ProcessGestureEvent(gestureEvent); +#endif + + if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) + { + // Identify the EMSCRIPTEN_EVENT_TOUCHEND and remove it from the list + for (int i = 0; i < CORE.Input.Touch.pointCount; i++) + { + if (touchEvent->touches[i].isChanged) + { + // Move all touch points one position up + for (int j = i; j < CORE.Input.Touch.pointCount - 1; j++) + { + CORE.Input.Touch.pointId[j] = CORE.Input.Touch.pointId[j + 1]; + CORE.Input.Touch.position[j] = CORE.Input.Touch.position[j + 1]; + } + // Decrease touch points count to remove the last one + CORE.Input.Touch.pointCount--; + break; + } + } + // Clamp pointCount to avoid negative values + if (CORE.Input.Touch.pointCount < 0) CORE.Input.Touch.pointCount = 0; + } + + return 1; // The event was consumed by the callback handler +} +//------------------------------------------------------------------------------------------------------- + +// EOF From fc843dc5572379482377ba4f33f6f53f47e3f69a Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 24 Dec 2025 19:21:43 +0100 Subject: [PATCH 008/117] Create SECURITY.md --- SECURITY.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..48a825e37 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,18 @@ +# Security Policy + +## Supported Versions + +Most considerations of errors and defects can be handled using the project Issues and/or Discussions. + +| Version | Supported | +| ------- | ------------------ | +| 6.0.x | :white_check_mark: | +| < 5.5 | :x: | + +## Reporting a Vulnerability + +Discovered vulnerability can be directly reported using the project Issues and/or Discussions. + +_TODO: Tell them where to go, how often they can expect to get an update on a +reported vulnerability, what to expect if the vulnerability is accepted or +declined, etc._ From 20dd4641c8caff962b5037bf1f1eb5471ae3598e Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 24 Dec 2025 19:35:06 +0100 Subject: [PATCH 009/117] REVIEWED: Potential security concerns while copying unbounded text data between strings Note that issue has been reported by CodeQL static analysis system --- src/platforms/rcore_desktop_glfw.c | 2 +- src/platforms/rcore_desktop_sdl.c | 8 ++++---- src/platforms/rcore_web.c | 2 +- src/platforms/rcore_web_emscripten.c | 2 +- src/rtext.c | 17 +++++++++++------ 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index efa146fd0..471050839 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1962,7 +1962,7 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) { CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); - strcpy(CORE.Window.dropFilepaths[i], paths[i]); + strncpy(CORE.Window.dropFilepaths[i], paths[i], MAX_FILEPATH_LENGTH - 1); } } } diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index add1de6ad..612707cea 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1431,9 +1431,9 @@ void PollInputEvents(void) // Event memory is now managed by SDL, so you should not free the data in SDL_EVENT_DROP_FILE, // and if you want to hold onto the text in SDL_EVENT_TEXT_EDITING and SDL_EVENT_TEXT_INPUT events, // you should make a copy of it. SDL_TEXTINPUTEVENT_TEXT_SIZE is no longer necessary and has been removed - strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data); + strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data, MAX_FILEPATH_LENGTH - 1); #else - strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file); + strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file, MAX_FILEPATH_LENGTH - 1); SDL_free(event.drop.file); #endif @@ -1444,9 +1444,9 @@ void PollInputEvents(void) CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); #if defined(USING_VERSION_SDL3) - strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data); + strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data, MAX_FILEPATH_LENGTH - 1); #else - strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file); + strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file, MAX_FILEPATH_LENGTH - 1); SDL_free(event.drop.file); #endif diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 934f778c3..adfdace74 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -1531,7 +1531,7 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) { CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); - strcpy(CORE.Window.dropFilepaths[i], paths[i]); + strncpy(CORE.Window.dropFilepaths[i], paths[i], MAX_FILEPATH_LENGTH - 1); } } } diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c index 1ed719631..25b477734 100644 --- a/src/platforms/rcore_web_emscripten.c +++ b/src/platforms/rcore_web_emscripten.c @@ -1387,7 +1387,7 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) { CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); - strcpy(CORE.Window.dropFilepaths[i], paths[i]); + strncpy(CORE.Window.dropFilepaths[i], paths[i], MAX_FILEPATH_LENGTH - 1); } } } diff --git a/src/rtext.c b/src/rtext.c index e4b439d28..453ed4507 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1597,14 +1597,13 @@ float TextToFloat(const char *text) #if defined(SUPPORT_TEXT_MANIPULATION) // Copy one string to another, returns bytes copied +// NOTE: Alternative implementation to strcpy(dst, src) from C standard library int TextCopy(char *dst, const char *src) { int bytes = 0; if ((src != NULL) && (dst != NULL)) { - // NOTE: Alternative: use strcpy(dst, src) - while (*src != '\0') { *dst = *src; @@ -1717,11 +1716,13 @@ char *TextReplace(const char *text, const char *search, const char *replacement) { char *insertPoint = NULL; // Next insert point char *temp = NULL; // Temp pointer + int textLen = 0; // Text string length int searchLen = 0; // Search string length of (the string to remove) int replaceLen = 0; // Replacement length (the string to replace by) int lastReplacePos = 0; // Distance between next search and end of last replace int count = 0; // Number of replacements + textLen = TextLength(text); searchLen = TextLength(search); if (searchLen == 0) return NULL; // Empty search causes infinite loop during count @@ -1732,7 +1733,8 @@ char *TextReplace(const char *text, const char *search, const char *replacement) for (count = 0; (temp = strstr(insertPoint, search)); count++) insertPoint = temp + searchLen; // Allocate returning string and point temp to it - temp = result = (char *)RL_MALLOC(TextLength(text) + (replaceLen - searchLen)*count + 1); + int tempLen = textLen + (replaceLen - searchLen)*count + 1; + temp = result = (char *)RL_MALLOC(tempLen); if (!result) return NULL; // Memory could not be allocated @@ -1744,13 +1746,16 @@ char *TextReplace(const char *text, const char *search, const char *replacement) { insertPoint = (char *)strstr(text, search); lastReplacePos = (int)(insertPoint - text); - temp = strncpy(temp, text, lastReplacePos) + lastReplacePos; - temp = strcpy(temp, replacement) + replaceLen; + temp = strncpy(temp, text, tempLen - 1) + lastReplacePos; + tempLen -= lastReplacePos; + temp = strncpy(temp, replacement, tempLen - 1) + replaceLen; + tempLen -= replaceLen; + text += lastReplacePos + searchLen; // Move to next "end of replace" } // Copy remaind text part after replacement to result (pointed by moving temp) - strcpy(temp, text); + strncpy(temp, text, tempLen - 1); } return result; From 101502103a559afdff6938dc0c739eb5d0870912 Mon Sep 17 00:00:00 2001 From: Dan Vu Date: Wed, 24 Dec 2025 20:58:40 +0100 Subject: [PATCH 010/117] Fixed FLAG_IS_SET to check if all bits in the flag are set in the value (#5441) --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 1f47efcb9..6f16b605b 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -273,7 +273,7 @@ #define FLAG_SET(n, f) ((n) |= (f)) #define FLAG_CLEAR(n, f) ((n) &= ~(f)) #define FLAG_TOGGLE(n, f) ((n) ^= (f)) -#define FLAG_IS_SET(n, f) (((n) & (f)) > 0) +#define FLAG_IS_SET(n, f) (((n) & (f)) == (f)) //---------------------------------------------------------------------------------- // Types and Structures Definition From 5e14ac5a2ed19071e8159455f5534aedf6b301dd Mon Sep 17 00:00:00 2001 From: Alvin De Cruz Date: Sat, 27 Dec 2025 03:42:32 +0800 Subject: [PATCH 011/117] #5387 - Fix keyboard input detected as gamepad on some Android devices (#5439) * [rcore][android] Fix keyboard input detected as gamepad on some devices (#5387) * [core] Add keyboard vs gamepad input test example (#5387) --- .../core/core_input_keyboard_gamepad_test.c | 173 ++++++++++++++++++ src/platforms/rcore_android.c | 7 +- 2 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 examples/core/core_input_keyboard_gamepad_test.c diff --git a/examples/core/core_input_keyboard_gamepad_test.c b/examples/core/core_input_keyboard_gamepad_test.c new file mode 100644 index 000000000..d1f9106f7 --- /dev/null +++ b/examples/core/core_input_keyboard_gamepad_test.c @@ -0,0 +1,173 @@ +/******************************************************************************************* +* +* raylib [core] example - Keyboard vs Gamepad Input Test +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* This example is a diagnostic tool to verify that keyboard input is not +* incorrectly detected as gamepad input on Android devices. +* +* Issue reference: https://github.com/raysan5/raylib/issues/5387 +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025 raylib contributors +* +********************************************************************************************/ + +#include "raylib.h" + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - keyboard vs gamepad test"); + + Vector2 ballPosition = { (float)screenWidth/2, (float)screenHeight/2 }; + int lastKeyPressed = 0; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + + // Track keyboard input + if (IsKeyDown(KEY_RIGHT)) ballPosition.x += 4.0f; + if (IsKeyDown(KEY_LEFT)) ballPosition.x -= 4.0f; + if (IsKeyDown(KEY_UP)) ballPosition.y -= 4.0f; + if (IsKeyDown(KEY_DOWN)) ballPosition.y += 4.0f; + + // Keep ball on screen + if (ballPosition.x < 25) ballPosition.x = 25; + if (ballPosition.x > screenWidth - 25) ballPosition.x = screenWidth - 25; + if (ballPosition.y < 25) ballPosition.y = 25; + if (ballPosition.y > screenHeight - 25) ballPosition.y = screenHeight - 25; + + // Track last key pressed + int key = GetKeyPressed(); + if (key != 0) lastKeyPressed = key; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + // Title + DrawText("KEYBOARD vs GAMEPAD INPUT TEST", 180, 10, 20, DARKGRAY); + DrawText("Issue #5387: Keyboard detected as gamepad on some Android devices", 120, 35, 14, GRAY); + + // Divider + DrawLine(0, 60, screenWidth, 60, LIGHTGRAY); + + // Keyboard section + DrawText("KEYBOARD INPUT", 20, 75, 18, DARKBLUE); + DrawRectangle(20, 100, 360, 80, Fade(BLUE, 0.1f)); + + DrawText(TextFormat("Arrow Keys: [%s] [%s] [%s] [%s]", + IsKeyDown(KEY_UP) ? "UP" : "--", + IsKeyDown(KEY_DOWN) ? "DN" : "--", + IsKeyDown(KEY_LEFT) ? "LT" : "--", + IsKeyDown(KEY_RIGHT) ? "RT" : "--"), 30, 110, 16, BLACK); + + DrawText(TextFormat("Last Key Pressed: %d", lastKeyPressed), 30, 135, 16, DARKGRAY); + DrawText(TextFormat("Any Key Down: %s", (IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || + IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT)) ? "YES" : "NO"), 30, 155, 16, DARKGRAY); + + // Gamepad section + DrawText("GAMEPAD STATUS", 420, 75, 18, DARKGREEN); + DrawRectangle(420, 100, 360, 80, Fade(GREEN, 0.1f)); + + bool gamepadReady = IsGamepadAvailable(0); + DrawText(TextFormat("Gamepad 0 Available: %s", gamepadReady ? "YES" : "NO"), + 430, 110, 16, gamepadReady ? RED : DARKGREEN); + + if (gamepadReady) + { + DrawText(TextFormat("D-Pad: [%s] [%s] [%s] [%s]", + IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_UP) ? "UP" : "--", + IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_DOWN) ? "DN" : "--", + IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_LEFT) ? "LT" : "--", + IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_RIGHT) ? "RT" : "--"), + 430, 135, 16, RED); + + DrawText(TextFormat("Gamepad Name: %.20s", GetGamepadName(0)), 430, 155, 14, DARKGRAY); + } + else + { + DrawText("No gamepad detected", 430, 135, 16, DARKGREEN); + } + + // Divider + DrawLine(0, 190, screenWidth, 190, LIGHTGRAY); + + // Test result section + DrawText("TEST RESULT", 20, 200, 18, MAROON); + + bool keyboardActive = IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || + IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT); + + if (keyboardActive && gamepadReady) + { + // BUG DETECTED: Keyboard is triggering gamepad detection + DrawRectangle(20, 225, 760, 50, Fade(RED, 0.3f)); + DrawText("BUG DETECTED: Keyboard input is being detected as gamepad!", 30, 235, 18, RED); + DrawText("The fix for issue #5387 may not be working correctly.", 30, 258, 14, DARKGRAY); + } + else if (keyboardActive && !gamepadReady) + { + // CORRECT: Keyboard works without triggering gamepad + DrawRectangle(20, 225, 760, 50, Fade(GREEN, 0.3f)); + DrawText("PASS: Keyboard input detected correctly (no phantom gamepad)", 30, 235, 18, DARKGREEN); + DrawText("Issue #5387 fix is working as expected.", 30, 258, 14, DARKGRAY); + } + else if (!keyboardActive && gamepadReady) + { + // Gamepad is connected (might be real or might be bug on idle) + DrawRectangle(20, 225, 760, 50, Fade(ORANGE, 0.3f)); + DrawText("INFO: Gamepad detected - press keyboard keys to test", 30, 235, 18, ORANGE); + DrawText("If gamepad stays active while pressing keyboard = BUG", 30, 258, 14, DARKGRAY); + } + else + { + // Idle state + DrawRectangle(20, 225, 760, 50, Fade(GRAY, 0.1f)); + DrawText("WAITING: Press arrow keys to test keyboard input", 30, 235, 18, GRAY); + DrawText("Gamepad should NOT become available when pressing keyboard keys", 30, 258, 14, DARKGRAY); + } + + // Ball controlled by keyboard + DrawText("Ball Control (Arrow Keys):", 20, 295, 16, DARKGRAY); + DrawCircleV(ballPosition, 25, MAROON); + DrawCircleLines((int)ballPosition.x, (int)ballPosition.y, 25, DARKGRAY); + + // Instructions + DrawRectangle(0, screenHeight - 45, screenWidth, 45, Fade(BLACK, 0.05f)); + DrawText("Instructions: Press keyboard arrow keys - the ball should move and gamepad should stay 'NO'", + 20, screenHeight - 35, 14, DARKGRAY); + DrawText("If gamepad becomes 'YES' while pressing keyboard = issue #5387 is NOT fixed", + 20, screenHeight - 18, 14, DARKGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 20a85a6a4..f3911d41b 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -1238,8 +1238,11 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) //int32_t AKeyEvent_getMetaState(event); // Handle gamepad button presses and releases - if (FLAG_IS_SET(source, AINPUT_SOURCE_JOYSTICK) || - FLAG_IS_SET(source, AINPUT_SOURCE_GAMEPAD)) + // NOTE: Skip gamepad handling if this is a keyboard event, as some devices + // report both AINPUT_SOURCE_KEYBOARD and AINPUT_SOURCE_GAMEPAD flags + if ((FLAG_IS_SET(source, AINPUT_SOURCE_JOYSTICK) || + FLAG_IS_SET(source, AINPUT_SOURCE_GAMEPAD)) && + !FLAG_IS_SET(source, AINPUT_SOURCE_KEYBOARD)) { // For now we'll assume a single gamepad which we "detect" on its input event CORE.Input.Gamepad.ready[0] = true; From aee6734cffb5666bdd04115c60db6257bcd5401e Mon Sep 17 00:00:00 2001 From: Dino <84743074+LeapersEdge@users.noreply.github.com> Date: Fri, 26 Dec 2025 20:46:09 +0100 Subject: [PATCH 012/117] fix: set correct default axes for gamepads that are not connected (inside rcore_desktop_glfw.c) (#5444) * fix: set correct default axes for gamepads that are not connected `glfwGetGamepadState` will set all gamepad state variables to 0.0 if required gamepad is not connected, but `RecordAutomationEvent()` inside rcore.c expects trigger axes to be -1.0f when gamepad is not connected. Since SDL and RGFW return -1.0f in such case, this change is aligning it with them * updated comment in rcore_desktop_glfw.c --- src/platforms/rcore_desktop_glfw.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 471050839..368dd5de8 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1266,8 +1266,14 @@ void PollInputEvents(void) // Get current gamepad state // NOTE: There is no callback available, so we get it manually GLFWgamepadstate state = { 0 }; - glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller - + int isGamepadConnected = glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller + if (!isGamepadConnected) + { + // setting axes to expected resting value instead of GLFW's 0.0f default when gamepad isnt connected + state.axes[GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f; + state.axes[GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f; + } + const unsigned char *buttons = state.buttons; for (int k = 0; (buttons != NULL) && (k < MAX_GAMEPAD_BUTTONS); k++) From 64bd27bd08aa7bf25d4daa6b5d28d3aa6ae4bb12 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 26 Dec 2025 20:49:03 +0100 Subject: [PATCH 013/117] Update rcore_desktop_glfw.c --- src/platforms/rcore_desktop_glfw.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 368dd5de8..824184368 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1266,10 +1266,10 @@ void PollInputEvents(void) // Get current gamepad state // NOTE: There is no callback available, so we get it manually GLFWgamepadstate state = { 0 }; - int isGamepadConnected = glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller - if (!isGamepadConnected) + int result = glfwGetGamepadState(i, &state); // This remaps all gamepads so they have their buttons mapped like an xbox controller + if (result == GLFW_FALSE) // No joystick is connected, no gamepad mapping or an error occurred { - // setting axes to expected resting value instead of GLFW's 0.0f default when gamepad isnt connected + // Setting axes to expected resting value instead of GLFW 0.0f default when gamepad is not connected state.axes[GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f; state.axes[GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f; } From 25a54d87e6ebeb70ef20443fb4e010195b958ead Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 26 Dec 2025 21:09:53 +0100 Subject: [PATCH 014/117] Update rcore_desktop_win32.c --- src/platforms/rcore_desktop_win32.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 29702921f..ce9d86cc2 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -1877,13 +1877,23 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara } break; case WM_DPICHANGED: { + // Get current dpi scale factor + float scalex = HIWORD(wParam)/96.0f; + float scaley = LOWORD(wParam)/96.0f; + RECT *suggestedRect = (RECT *)lparam; // Never set the window size to anything other than the suggested rect here // Doing so can cause a window to stutter between monitors when transitioning between them - int result = (int)SetWindowPos(hwnd, NULL, suggestedRect->left, suggestedRect->top, - suggestedRect->right - suggestedRect->left, suggestedRect->bottom - suggestedRect->top, SWP_NOZORDER | SWP_NOACTIVATE); + int result = (int)SetWindowPos(hwnd, NULL, + suggestedRect->left, suggestedRect->top, + suggestedRect->right - suggestedRect->left, + suggestedRect->bottom - suggestedRect->top, + SWP_NOZORDER | SWP_NOACTIVATE); + if (result == 0) TRACELOG(LOG_ERROR, "Failed to set window position [ERROR: %lu]", GetLastError()); + + // TODO: Update screen data, render size, screen scaling, viewport... } break; case WM_SETCURSOR: From 84dfe6a4cf6ab18c8e7d7c2701a93eae5c28dbc0 Mon Sep 17 00:00:00 2001 From: TheLazyIndianTechie Date: Sat, 27 Dec 2025 18:52:24 +0530 Subject: [PATCH 015/117] [rmodels] Fix glTF animation framerate calculation (#4472) (#5445) - Changed GLTF_ANIMDELAY (17ms, ~58.82fps) to GLTF_FRAMERATE (60.0fps) - Updated frameCount calculation: (animDuration * 60) instead of (animDuration * 1000 / 17) - Updated time calculation: j / 60.0f instead of (j * 17) / 1000.0f This fixes animation frame count misalignment when importing glTF models exported at standard 60fps. Animations that were 27+ frames shorter than expected on 1350-frame sequences will now import correctly. --- src/rmodels.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 40af4afc4..665b94147 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -6353,7 +6353,7 @@ static bool GetPoseAtTimeGLTF(cgltf_interpolation_type interpolationType, cgltf_ return true; } -#define GLTF_ANIMDELAY 17 // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms) +#define GLTF_FRAMERATE 60.0f // glTF animation framerate (frames per second) static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCount) { @@ -6473,13 +6473,13 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo if (animData.name != NULL) strncpy(animations[i].name, animData.name, sizeof(animations[i].name) - 1); - animations[i].frameCount = (int)(animDuration*1000.0f/GLTF_ANIMDELAY) + 1; + animations[i].frameCount = (int)(animDuration*GLTF_FRAMERATE) + 1; animations[i].framePoses = (Transform **)RL_MALLOC(animations[i].frameCount*sizeof(Transform *)); for (int j = 0; j < animations[i].frameCount; j++) { animations[i].framePoses[j] = (Transform *)RL_MALLOC(animations[i].boneCount*sizeof(Transform)); - float time = ((float) j*GLTF_ANIMDELAY)/1000.0f; + float time = (float)j / GLTF_FRAMERATE; for (int k = 0; k < animations[i].boneCount; k++) { From 538bf820374db46493f46e89f228934ca686726e Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 27 Dec 2025 14:35:38 +0100 Subject: [PATCH 016/117] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 875792f18..815088fc0 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ contributors ------------ - + license From e4491b40b52078370fe4b4e088d17bde6d562310 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 27 Dec 2025 14:43:46 +0100 Subject: [PATCH 017/117] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 815088fc0..694937a70 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ contributors ------------ - + license From 05f5143603ba4db9b3157e981926a42c55c4c766 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 27 Dec 2025 15:05:18 +0100 Subject: [PATCH 018/117] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 694937a70..37e37c7c4 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ contributors ------------ - + license From da1a76604f76c82c490f7cf0d063fae49326c1ed Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Dec 2025 16:05:42 +0100 Subject: [PATCH 019/117] REMOVED: `CORE.Window.fullscreen`, using available flag instead --- src/platforms/rcore_android.c | 1 - src/platforms/rcore_desktop_glfw.c | 20 ++++++++----------- src/platforms/rcore_desktop_rgfw.c | 10 ++++------ src/platforms/rcore_desktop_sdl.c | 11 ++--------- src/platforms/rcore_template.c | 1 - src/platforms/rcore_web.c | 20 +++++-------------- src/platforms/rcore_web_emscripten.c | 12 ++---------- src/rcore.c | 29 ++++++++++++++-------------- 8 files changed, 35 insertions(+), 69 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index f3911d41b..cca4f4d39 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -885,7 +885,6 @@ void ClosePlatform(void) // NOTE: returns false in case graphic device could not be created static int InitGraphicsDevice(void) { - CORE.Window.fullscreen = true; FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); EGLint samples = 0; diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 824184368..ed8b1b542 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -176,7 +176,7 @@ bool WindowShouldClose(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - if (!CORE.Window.fullscreen) + if (!FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) { // Store previous window position (in case we exit fullscreen) CORE.Window.previousPosition = CORE.Window.position; @@ -192,8 +192,6 @@ void ToggleFullscreen(void) { TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor"); - CORE.Window.fullscreen = false; - FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); glfwSetWindowMonitor(platform.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } @@ -666,7 +664,7 @@ void SetWindowMonitor(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) { - if (CORE.Window.fullscreen) + if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) { TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); @@ -1422,8 +1420,6 @@ int InitPlatform(void) unsigned int requestedWindowFlags = CORE.Window.flags; // Check window creation flags - if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) CORE.Window.fullscreen = true; - if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Window initially hidden @@ -1536,11 +1532,14 @@ int InitPlatform(void) // REF: https://github.com/raysan5/raylib/issues/1554 glfwSetJoystickCallback(NULL); - GLFWmonitor *monitor = NULL; - if (CORE.Window.fullscreen) + if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0)) FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); + + // Init window in fullscreen mode if requested + // NOTE: Keeping original screen size for toggle + if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) { // NOTE: Fullscreen applications default to the primary monitor - monitor = glfwGetPrimaryMonitor(); + GLFWmonitor *monitor = glfwGetPrimaryMonitor(); if (!monitor) { TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor"); @@ -1614,9 +1613,6 @@ int InitPlatform(void) TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); return -1; } - - // NOTE: Full-screen change, not working properly... - //glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } else { diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 39ac8fb32..04e461ded 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -290,14 +290,13 @@ bool WindowShouldClose(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - if (!CORE.Window.fullscreen) + if (!FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) { // Store previous window position (in case we exit fullscreen) CORE.Window.previousPosition = CORE.Window.position; CORE.Window.previousScreen = CORE.Window.screen; platform.mon = RGFW_window_getMonitor(platform.window); - CORE.Window.fullscreen = true; FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); RGFW_monitor_scaleToWindow(platform.mon, platform.window); @@ -305,7 +304,6 @@ void ToggleFullscreen(void) } else { - CORE.Window.fullscreen = false; FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); if (platform.mon.mode.area.w) @@ -331,7 +329,9 @@ void ToggleFullscreen(void) // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - if (CORE.Window.fullscreen) + if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) ToggleFullscreen(); + + if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) { CORE.Window.previousPosition = CORE.Window.position; CORE.Window.previousScreen = CORE.Window.screen; @@ -348,8 +348,6 @@ void ToggleBorderlessWindowed(void) CORE.Window.position = CORE.Window.previousPosition; RGFW_window_resize(platform.window, RGFW_AREA(CORE.Window.previousScreen.width, CORE.Window.previousScreen.height)); } - - CORE.Window.fullscreen = !CORE.Window.fullscreen; } // Set window state: maximized, if resizable diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 612707cea..952268ca6 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -472,13 +472,11 @@ void ToggleFullscreen(void) { SDL_SetWindowFullscreen(platform.window, 0); FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); - CORE.Window.fullscreen = false; } else { SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); - CORE.Window.fullscreen = true; } } else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); @@ -554,7 +552,7 @@ void SetWindowState(unsigned int flags) #endif { SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); - CORE.Window.fullscreen = true; + FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); } else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); } @@ -644,7 +642,6 @@ void ClearWindowState(unsigned int flags) if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE)) { SDL_SetWindowFullscreen(platform.window, 0); - CORE.Window.fullscreen = false; } if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)) { @@ -1937,11 +1934,7 @@ int InitPlatform(void) FLAG_SET(flags, SDL_WINDOW_MOUSE_CAPTURE); // Window has mouse captured // Check window creation flags - if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) - { - CORE.Window.fullscreen = true; - FLAG_SET(flags, SDL_WINDOW_FULLSCREEN); - } + if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) FLAG_SET(flags, SDL_WINDOW_FULLSCREEN); //if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) FLAG_SET(flags, SDL_WINDOW_HIDDEN); if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED)) FLAG_SET(flags, SDL_WINDOW_BORDERLESS); diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 1f8c5242b..b22d3f2f5 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -454,7 +454,6 @@ int InitPlatform(void) // raylib uses OpenGL so, platform should create that kind of connection // Below example illustrates that process using EGL library //---------------------------------------------------------------------------- - CORE.Window.fullscreen = true; FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index adfdace74..e138de302 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -204,7 +204,6 @@ void ToggleFullscreen(void) EM_ASM(document.exitFullscreen();); - CORE.Window.fullscreen = false; FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE); } @@ -213,14 +212,12 @@ void ToggleFullscreen(void) if (enterFullscreen) { // NOTE: The setTimeouts handle the browser mode change delay - EM_ASM - ( - setTimeout(function() - { + EM_ASM( + setTimeout(function(){ Module.requestFullscreen(false, false); }, 100); ); - CORE.Window.fullscreen = true; + FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); } @@ -238,7 +235,7 @@ void ToggleFullscreen(void) */ // EM_ASM(Module.requestFullscreen(false, false);); /* - if (!CORE.Window.fullscreen) + if (!FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) { // Option 1: Request fullscreen for the canvas element // This option does not seem to work at all: @@ -274,7 +271,6 @@ void ToggleFullscreen(void) emscripten_get_canvas_element_size(platform.canvasId, &width, &height); TRACELOG(LOG_WARNING, "Emscripten: Enter fullscreen: Canvas size: %i x %i", width, height); - CORE.Window.fullscreen = true; // Toggle fullscreen flag FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); } else @@ -286,7 +282,6 @@ void ToggleFullscreen(void) emscripten_get_canvas_element_size(platform.canvasId, &width, &height); TRACELOG(LOG_WARNING, "Emscripten: Exit fullscreen: Canvas size: %i x %i", width, height); - CORE.Window.fullscreen = false; // Toggle fullscreen flag FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); } */ @@ -313,7 +308,6 @@ void ToggleBorderlessWindowed(void) EM_ASM(document.exitFullscreen();); - CORE.Window.fullscreen = false; FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE); } @@ -545,7 +539,6 @@ void ClearWindowState(unsigned int flags) if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE) || (canvasStyleWidth > canvasWidth)) EM_ASM(document.exitFullscreen();); } - CORE.Window.fullscreen = false; FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); } @@ -1155,8 +1148,6 @@ int InitPlatform(void) // glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers // Check window creation flags - if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) CORE.Window.fullscreen = true; - if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Window initially hidden @@ -1260,7 +1251,7 @@ int InitPlatform(void) // TODO: Consider requesting another type of canvas, not a WebGL one --> Replace GLFW-web by Emscripten? platform.pixels = (unsigned int *)RL_CALLOC(CORE.Window.screen.width*CORE.Window.screen.height, sizeof(unsigned int)); #else - if (CORE.Window.fullscreen) + if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) { // remember center for switchinging from fullscreen to window if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) @@ -1830,7 +1821,6 @@ static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const Emscripte const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0); if (!wasFullscreen) { - CORE.Window.fullscreen = false; FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE); } diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c index 25b477734..aeead6d9b 100644 --- a/src/platforms/rcore_web_emscripten.c +++ b/src/platforms/rcore_web_emscripten.c @@ -167,7 +167,6 @@ void ToggleFullscreen(void) EM_ASM(document.exitFullscreen();); - CORE.Window.fullscreen = false; FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE); } @@ -183,7 +182,7 @@ void ToggleFullscreen(void) Module.requestFullscreen(false, false); }, 100); ); - CORE.Window.fullscreen = true; + FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); } @@ -201,7 +200,7 @@ void ToggleFullscreen(void) */ // EM_ASM(Module.requestFullscreen(false, false);); /* - if (!CORE.Window.fullscreen) + if (!FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) { // Option 1: Request fullscreen for the canvas element // This option does not seem to work at all: @@ -237,7 +236,6 @@ void ToggleFullscreen(void) emscripten_get_canvas_element_size("#canvas", &width, &height); TRACELOG(LOG_WARNING, "Emscripten: Enter fullscreen: Canvas size: %i x %i", width, height); - CORE.Window.fullscreen = true; // Toggle fullscreen flag FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); } else @@ -249,7 +247,6 @@ void ToggleFullscreen(void) emscripten_get_canvas_element_size("#canvas", &width, &height); TRACELOG(LOG_WARNING, "Emscripten: Exit fullscreen: Canvas size: %i x %i", width, height); - CORE.Window.fullscreen = false; // Toggle fullscreen flag FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); } */ @@ -275,7 +272,6 @@ void ToggleBorderlessWindowed(void) EM_ASM(document.exitFullscreen();); - CORE.Window.fullscreen = false; FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE); } @@ -494,7 +490,6 @@ void ClearWindowState(unsigned int flags) if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE) || (canvasStyleWidth > canvasWidth)) EM_ASM(document.exitFullscreen();); } - CORE.Window.fullscreen = false; FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); } @@ -1117,8 +1112,6 @@ int InitPlatform(void) attribs.antialias = EM_FALSE; // Check window creation flags - //if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) CORE.Window.fullscreen = true; - // Disable FLAG_WINDOW_MINIMIZED, not supported if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); @@ -1354,7 +1347,6 @@ static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const Emscripte const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0); if (!wasFullscreen) { - CORE.Window.fullscreen = false; FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE); } diff --git a/src/rcore.c b/src/rcore.c index 6f16b605b..d0a57048d 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -287,20 +287,19 @@ typedef struct CoreData { const char *title; // Window text title const pointer unsigned int flags; // Configuration flags (bit based), keeps window state bool ready; // Check if window has been initialized successfully - bool fullscreen; // Check if fullscreen mode is enabled bool shouldClose; // Check if window set for closing bool resizedLastFrame; // Check if window has been resized last frame bool eventWaiting; // Wait for events before ending frame bool usingFbo; // Using FBO (RenderTexture) for rendering instead of default framebuffer - Point position; // Window position (required on fullscreen toggle) - Point previousPosition; // Window previous position (required on borderless windowed toggle) Size display; // Display width and height (monitor, device-screen, LCD, ...) - Size screen; // Screen width and height (used render area) - Size previousScreen; // Screen previous width and height (required on borderless windowed toggle) - Size currentFbo; // Current render width and height (depends on active fbo) - Size render; // Framebuffer width and height (render area, including black bars if required) - Point renderOffset; // Offset from render area (must be divided by 2) + Size screen; // Screen current width and height + Point position; // Window current position + Size previousScreen; // Screen previous width and height (required on fullscreen/borderless-windowed toggle) + Point previousPosition; // Window previous position (required on fullscreeen/borderless-windowed toggle) + Size render; // Screen framebuffer width and height + Point renderOffset; // Screen framebuffer render offset (Not required anymore?) + Size currentFbo; // Current framebuffer render width and height (depends on active render texture) Size screenMin; // Screen minimum width and height (for resizable window) Size screenMax; // Screen maximum width and height (for resizable window) Matrix screenScale; // Matrix to scale screen (framebuffer rendering) @@ -762,31 +761,31 @@ bool IsWindowReady(void) // Check if window is currently fullscreen bool IsWindowFullscreen(void) { - return CORE.Window.fullscreen; + return FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); } // Check if window is currently hidden bool IsWindowHidden(void) { - return (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)); + return FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN); } // Check if window has been minimized bool IsWindowMinimized(void) { - return (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)); + return FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); } // Check if window has been maximized bool IsWindowMaximized(void) { - return (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)); + return FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); } // Check if window has the focus bool IsWindowFocused(void) { - return (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)); + return !FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); } // Check if window has been resizedLastFrame @@ -798,7 +797,7 @@ bool IsWindowResized(void) // Check if one specific window flag is enabled bool IsWindowState(unsigned int flag) { - return (FLAG_IS_SET(CORE.Window.flags, flag)); + return FLAG_IS_SET(CORE.Window.flags, flag); } // Get current screen width @@ -1100,7 +1099,7 @@ void BeginScissorMode(int x, int y, int width, int height) rlScissor((int)(x*scale.x), (int)(GetScreenHeight()*scale.y - (((y + height)*scale.y))), (int)(width*scale.x), (int)(height*scale.y)); } #else - if (!CORE.Window.usingFbo && (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))) + if (!CORE.Window.usingFbo && FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) { Vector2 scale = GetWindowScaleDPI(); rlScissor((int)(x*scale.x), (int)(CORE.Window.currentFbo.height - (y + height)*scale.y), (int)(width*scale.x), (int)(height*scale.y)); From 37bc3f50120f752bc2767a32a11f1fcffd6906e0 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Dec 2025 16:07:59 +0100 Subject: [PATCH 020/117] REMOVED: `SetupFramebuffer()`, most platforms do not need it any more Kept only for platforms that could potentially need it --- src/platforms/rcore_android.c | 80 +++++++++++++++++++++++++++++ src/platforms/rcore_desktop_rgfw.c | 12 ++--- src/platforms/rcore_desktop_win32.c | 3 +- src/platforms/rcore_drm.c | 80 +++++++++++++++++++++++++++++ src/rcore.c | 79 ---------------------------- 5 files changed, 164 insertions(+), 90 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index cca4f4d39..f150bf638 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -267,6 +267,8 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd); static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs static GamepadButton AndroidTranslateGamepadButton(int button); // Map Android gamepad button to raylib gamepad button +static void SetupFramebuffer(int width, int height); // Setup main framebuffer (required by InitPlatform()) + //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- @@ -1419,4 +1421,82 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) return 0; } +// Compute framebuffer size relative to screen size and display size +// NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified +static void SetupFramebuffer(int width, int height) +{ + // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var) + if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height)) + { + TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height); + + // Downscaling to fit display with border-bars + float widthRatio = (float)CORE.Window.display.width/(float)CORE.Window.screen.width; + float heightRatio = (float)CORE.Window.display.height/(float)CORE.Window.screen.height; + + if (widthRatio <= heightRatio) + { + CORE.Window.render.width = CORE.Window.display.width; + CORE.Window.render.height = (int)round((float)CORE.Window.screen.height*widthRatio); + CORE.Window.renderOffset.x = 0; + CORE.Window.renderOffset.y = (CORE.Window.display.height - CORE.Window.render.height); + } + else + { + CORE.Window.render.width = (int)round((float)CORE.Window.screen.width*heightRatio); + CORE.Window.render.height = CORE.Window.display.height; + CORE.Window.renderOffset.x = (CORE.Window.display.width - CORE.Window.render.width); + CORE.Window.renderOffset.y = 0; + } + + // Screen scaling required + float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width; + CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f); + + // NOTE: We render to full display resolution! + // We just need to calculate above parameters for downscale matrix and offsets + CORE.Window.render.width = CORE.Window.display.width; + CORE.Window.render.height = CORE.Window.display.height; + + TRACELOG(LOG_WARNING, "DISPLAY: Downscale matrix generated, content will be rendered at (%ix%i)", CORE.Window.render.width, CORE.Window.render.height); + } + else if ((CORE.Window.screen.width < CORE.Window.display.width) || (CORE.Window.screen.height < CORE.Window.display.height)) + { + // Required screen size is smaller than display size + TRACELOG(LOG_INFO, "DISPLAY: Upscaling required: Screen size (%ix%i) smaller than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height); + + if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0)) + { + CORE.Window.screen.width = CORE.Window.display.width; + CORE.Window.screen.height = CORE.Window.display.height; + } + + // Upscaling to fit display with border-bars + float displayRatio = (float)CORE.Window.display.width/(float)CORE.Window.display.height; + float screenRatio = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height; + + if (displayRatio <= screenRatio) + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = (int)round((float)CORE.Window.screen.width/displayRatio); + CORE.Window.renderOffset.x = 0; + CORE.Window.renderOffset.y = (CORE.Window.render.height - CORE.Window.screen.height); + } + else + { + CORE.Window.render.width = (int)round((float)CORE.Window.screen.height*displayRatio); + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.renderOffset.x = (CORE.Window.render.width - CORE.Window.screen.width); + CORE.Window.renderOffset.y = 0; + } + } + else + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.renderOffset.x = 0; + CORE.Window.renderOffset.y = 0; + } +} + // EOF diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 04e461ded..558b6de55 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -383,7 +383,7 @@ void SetWindowState(unsigned int flags) } if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE)) { - if (!CORE.Window.fullscreen) ToggleFullscreen(); + ToggleFullscreen(); } if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)) { @@ -457,7 +457,7 @@ void ClearWindowState(unsigned int flags) } if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE)) { - if (CORE.Window.fullscreen) ToggleFullscreen(); + ToggleFullscreen(); } if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)) { @@ -508,7 +508,7 @@ void ClearWindowState(unsigned int flags) } if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE)) { - if (CORE.Window.fullscreen) ToggleBorderlessWindowed(); + ToggleBorderlessWindowed(); } if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT)) { @@ -1256,13 +1256,11 @@ int InitPlatform(void) // Check window creation flags if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) { - CORE.Window.fullscreen = true; FLAG_SET(flags, RGFW_windowFullscreen); } if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) { - CORE.Window.fullscreen = true; FLAG_SET(flags, RGFW_windowedFullscreen); } @@ -1313,10 +1311,6 @@ int InitPlatform(void) CORE.Window.display.width = CORE.Window.screen.width; CORE.Window.display.height = CORE.Window.screen.height; #endif - // TODO: Is this needed by raylib now? - // If so, rcore_desktop_sdl should be updated too - //SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) RGFW_window_swapInterval(platform.window, 1); RGFW_window_makeCurrent(platform.window); diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index ce9d86cc2..973fafa68 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -2049,8 +2049,7 @@ static void HandleWindowResize(HWND hwnd, int *width, int *height) // TODO: Update framebuffer on resize CORE.Window.currentFbo.width = (int)clientSize.cx; CORE.Window.currentFbo.height = (int)clientSize.cy; - //glViewport(0, 0, clientSize.cx, clientSize.cy); - //SetupFramebuffer(0, 0); + //SetupViewport(0, 0, clientSize.cx, clientSize.cy); SetupViewport(clientSize.cx, clientSize.cy); CORE.Window.resizedLastFrame = true; diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 640799b0a..0aeab3ab4 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -265,6 +265,8 @@ static int FindMatchingConnectorMode(const drmModeConnector *connector, const dr static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search exactly matching DRM connector mode in connector's list static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search the nearest matching DRM connector mode in connector's list +static void SetupFramebuffer(int width, int height); // Setup main framebuffer (required by InitPlatform()) + //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- @@ -2479,4 +2481,82 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt return nearestIndex; } +// Compute framebuffer size relative to screen size and display size +// NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified +static void SetupFramebuffer(int width, int height) +{ + // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var) + if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height)) + { + TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height); + + // Downscaling to fit display with border-bars + float widthRatio = (float)CORE.Window.display.width/(float)CORE.Window.screen.width; + float heightRatio = (float)CORE.Window.display.height/(float)CORE.Window.screen.height; + + if (widthRatio <= heightRatio) + { + CORE.Window.render.width = CORE.Window.display.width; + CORE.Window.render.height = (int)round((float)CORE.Window.screen.height*widthRatio); + CORE.Window.renderOffset.x = 0; + CORE.Window.renderOffset.y = (CORE.Window.display.height - CORE.Window.render.height); + } + else + { + CORE.Window.render.width = (int)round((float)CORE.Window.screen.width*heightRatio); + CORE.Window.render.height = CORE.Window.display.height; + CORE.Window.renderOffset.x = (CORE.Window.display.width - CORE.Window.render.width); + CORE.Window.renderOffset.y = 0; + } + + // Screen scaling required + float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width; + CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f); + + // NOTE: We render to full display resolution! + // We just need to calculate above parameters for downscale matrix and offsets + CORE.Window.render.width = CORE.Window.display.width; + CORE.Window.render.height = CORE.Window.display.height; + + TRACELOG(LOG_WARNING, "DISPLAY: Downscale matrix generated, content will be rendered at (%ix%i)", CORE.Window.render.width, CORE.Window.render.height); + } + else if ((CORE.Window.screen.width < CORE.Window.display.width) || (CORE.Window.screen.height < CORE.Window.display.height)) + { + // Required screen size is smaller than display size + TRACELOG(LOG_INFO, "DISPLAY: Upscaling required: Screen size (%ix%i) smaller than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height); + + if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0)) + { + CORE.Window.screen.width = CORE.Window.display.width; + CORE.Window.screen.height = CORE.Window.display.height; + } + + // Upscaling to fit display with border-bars + float displayRatio = (float)CORE.Window.display.width/(float)CORE.Window.display.height; + float screenRatio = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height; + + if (displayRatio <= screenRatio) + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = (int)round((float)CORE.Window.screen.width/displayRatio); + CORE.Window.renderOffset.x = 0; + CORE.Window.renderOffset.y = (CORE.Window.render.height - CORE.Window.screen.height); + } + else + { + CORE.Window.render.width = (int)round((float)CORE.Window.screen.height*displayRatio); + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.renderOffset.x = (CORE.Window.render.width - CORE.Window.screen.width); + CORE.Window.renderOffset.y = 0; + } + } + else + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.renderOffset.x = 0; + CORE.Window.renderOffset.y = 0; + } +} + // EOF diff --git a/src/rcore.c b/src/rcore.c index d0a57048d..565eb915c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -492,7 +492,6 @@ extern int InitPlatform(void); // Initialize platform (graphics, inputs extern void ClosePlatform(void); // Close platform static void InitTimer(void); // Initialize timer, hi-resolution if available (required by InitPlatform()) -static void SetupFramebuffer(int width, int height); // Setup main framebuffer (required by InitPlatform()) static void SetupViewport(int width, int height); // Set viewport for a provided width and height static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories in a base path @@ -3827,84 +3826,6 @@ void SetupViewport(int width, int height) rlLoadIdentity(); // Reset current matrix (modelview) } -// Compute framebuffer size relative to screen size and display size -// NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified -void SetupFramebuffer(int width, int height) -{ - // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var) - if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height)) - { - TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height); - - // Downscaling to fit display with border-bars - float widthRatio = (float)CORE.Window.display.width/(float)CORE.Window.screen.width; - float heightRatio = (float)CORE.Window.display.height/(float)CORE.Window.screen.height; - - if (widthRatio <= heightRatio) - { - CORE.Window.render.width = CORE.Window.display.width; - CORE.Window.render.height = (int)round((float)CORE.Window.screen.height*widthRatio); - CORE.Window.renderOffset.x = 0; - CORE.Window.renderOffset.y = (CORE.Window.display.height - CORE.Window.render.height); - } - else - { - CORE.Window.render.width = (int)round((float)CORE.Window.screen.width*heightRatio); - CORE.Window.render.height = CORE.Window.display.height; - CORE.Window.renderOffset.x = (CORE.Window.display.width - CORE.Window.render.width); - CORE.Window.renderOffset.y = 0; - } - - // Screen scaling required - float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width; - CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f); - - // NOTE: We render to full display resolution! - // We just need to calculate above parameters for downscale matrix and offsets - CORE.Window.render.width = CORE.Window.display.width; - CORE.Window.render.height = CORE.Window.display.height; - - TRACELOG(LOG_WARNING, "DISPLAY: Downscale matrix generated, content will be rendered at (%ix%i)", CORE.Window.render.width, CORE.Window.render.height); - } - else if ((CORE.Window.screen.width < CORE.Window.display.width) || (CORE.Window.screen.height < CORE.Window.display.height)) - { - // Required screen size is smaller than display size - TRACELOG(LOG_INFO, "DISPLAY: Upscaling required: Screen size (%ix%i) smaller than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height); - - if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0)) - { - CORE.Window.screen.width = CORE.Window.display.width; - CORE.Window.screen.height = CORE.Window.display.height; - } - - // Upscaling to fit display with border-bars - float displayRatio = (float)CORE.Window.display.width/(float)CORE.Window.display.height; - float screenRatio = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height; - - if (displayRatio <= screenRatio) - { - CORE.Window.render.width = CORE.Window.screen.width; - CORE.Window.render.height = (int)round((float)CORE.Window.screen.width/displayRatio); - CORE.Window.renderOffset.x = 0; - CORE.Window.renderOffset.y = (CORE.Window.render.height - CORE.Window.screen.height); - } - else - { - CORE.Window.render.width = (int)round((float)CORE.Window.screen.height*displayRatio); - CORE.Window.render.height = CORE.Window.screen.height; - CORE.Window.renderOffset.x = (CORE.Window.render.width - CORE.Window.screen.width); - CORE.Window.renderOffset.y = 0; - } - } - else - { - CORE.Window.render.width = CORE.Window.screen.width; - CORE.Window.render.height = CORE.Window.screen.height; - CORE.Window.renderOffset.x = 0; - CORE.Window.renderOffset.y = 0; - } -} - // Scan all files and directories in a base path // WARNING: files.paths[] must be previously allocated and // contain enough space to store all required paths From 1d8e011eee6129005648b3dd105a19f355d82a41 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Dec 2025 16:08:08 +0100 Subject: [PATCH 021/117] Update rcore_drm.c --- src/platforms/rcore_drm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 0aeab3ab4..366477aac 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -1149,7 +1149,6 @@ int InitPlatform(void) // Initialize graphic device: display/window and graphic context //---------------------------------------------------------------------------- - CORE.Window.fullscreen = true; FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); #if defined(DEFAULT_GRAPHIC_DEVICE_DRM) From 8cfb99f275dcd48ce6c91a289d2c9914bae2035b Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Dec 2025 16:08:19 +0100 Subject: [PATCH 022/117] Minor comment tweaks --- src/rcore.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 565eb915c..ea38300f1 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -315,17 +315,17 @@ typedef struct CoreData { struct { struct { 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 currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state + char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state // NOTE: Since key press logic involves comparing previous vs currrent key state, // key repeats needs to be handled specially - char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame + char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame - int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue + int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue int keyPressedQueueCount; // Input keys queue count - int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) + int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) int charPressedQueueCount; // Input characters queue count } Keyboard; @@ -341,8 +341,8 @@ typedef struct CoreData { bool cursorLocked; // Track if cursor is locked (disabled) bool cursorOnScreen; // Tracks if cursor is inside client area - char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state - char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state + char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state + char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state Vector2 currentWheelMove; // Registers current mouse wheel variation Vector2 previousWheelMove; // Registers previous mouse wheel variation From 297dcc07b850beafc3ad79609f762e54c0be7f84 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Dec 2025 16:08:34 +0100 Subject: [PATCH 023/117] Update core_highdpi_testbed.c --- examples/core/core_highdpi_testbed.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/core/core_highdpi_testbed.c b/examples/core/core_highdpi_testbed.c index bf103fb31..d34951fc0 100644 --- a/examples/core/core_highdpi_testbed.c +++ b/examples/core/core_highdpi_testbed.c @@ -49,6 +49,7 @@ int main(void) scaleDpi = GetWindowScaleDPI(); if (IsKeyPressed(KEY_SPACE)) ToggleBorderlessWindowed(); + if (IsKeyPressed(KEY_F)) ToggleFullscreen(); //---------------------------------------------------------------------------------- // Draw @@ -58,12 +59,12 @@ int main(void) ClearBackground(RAYWHITE); // Draw grid - for (int h = 0; h < 20; h++) + for (int h = 0; h < GetScreenHeight()/gridSpacing + 1; h++) { DrawText(TextFormat("%02i", h*gridSpacing), 4, h*gridSpacing - 4, 10, GRAY); DrawLine(24, h*gridSpacing, GetScreenWidth(), h*gridSpacing, LIGHTGRAY); } - for (int v = 0; v < 40; v++) + for (int v = 0; v < GetScreenWidth()/gridSpacing + 1; v++) { DrawText(TextFormat("%02i", v*gridSpacing), v*gridSpacing - 10, 4, 10, GRAY); DrawLine(v*gridSpacing, 20, v*gridSpacing, GetScreenHeight(), LIGHTGRAY); @@ -76,6 +77,10 @@ int main(void) DrawText(TextFormat("RENDER SIZE: %ix%i", GetRenderWidth(), GetRenderHeight()), 50, 130, 20, DARKGRAY); DrawText(TextFormat("SCALE FACTOR: %.1fx%.1f", scaleDpi.x, scaleDpi.y), 50, 170, 20, GRAY); + // Draw reference rectangles, top-left and bottom-right corners + DrawRectangle(0, 0, 30, 60, RED); + DrawRectangle(GetScreenWidth() - 30, GetScreenHeight() - 60, 30, 60, BLUE); + // Draw mouse position DrawCircleV(GetMousePosition(), 20, MAROON); DrawRectangle(mousePos.x - 25, mousePos.y, 50, 2, BLACK); From 2cf8983e18c3a5869d0a6fa00549bb65e923d606 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Dec 2025 16:11:42 +0100 Subject: [PATCH 024/117] WARNING: REDESIGNED: Fullscreen modes, use current display resolution Considering multi-monitor and multi-ppi configurations Fullscreen-exclusive scales to available display resolution, ignoring content scaling Windowed-borderless scales to available logical resolution considering HighDPI **if requested** --- src/platforms/rcore_desktop_glfw.c | 226 +++++++++++++---------------- 1 file changed, 103 insertions(+), 123 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index ed8b1b542..9b360771f 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -178,41 +178,56 @@ void ToggleFullscreen(void) { if (!FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) { - // Store previous window position (in case we exit fullscreen) + // Store previous screen data (in case exiting fullscreen) CORE.Window.previousPosition = CORE.Window.position; + CORE.Window.previousScreen = CORE.Window.screen; + // Use current monitor the window is on to get fullscreen required size int monitorCount = 0; int monitorIndex = GetCurrentMonitor(); GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - // Use current monitor, so we correctly get the display the window is on GLFWmonitor *monitor = (monitorIndex < monitorCount)? monitors[monitorIndex] : NULL; - if (monitor == NULL) + if (monitor != NULL) { - TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor"); + // Get current monitor video mode + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitorIndex]); + CORE.Window.display.width = mode->width; + CORE.Window.display.height = mode->height; + CORE.Window.position = (Point){ 0, 0 }; + CORE.Window.screen = (Size){ CORE.Window.display.width, CORE.Window.display.height }; - glfwSetWindowMonitor(platform.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); - } - else - { - CORE.Window.fullscreen = true; + // Set fullscreen flag to be processed on FramebufferSizeCallback() accordingly FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); + // WARNING: This function launches FramebufferSizeCallback() glfwSetWindowMonitor(platform.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } + else TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor"); } else { - CORE.Window.fullscreen = false; + // Restore previous window position and size + CORE.Window.position = CORE.Window.previousPosition; + CORE.Window.screen = CORE.Window.previousScreen; + + // Set fullscreen flag to be processed on FramebufferSizeCallback() accordingly + // and considered by GetWindowScaleDPI() FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE); - glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); +#if !defined(__APPLE__) + // Make sure to restore render size considering HighDPI scaling + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) + { + Vector2 scaleDpi = GetWindowScaleDPI(); + CORE.Window.screen.width *= scaleDpi.x; + CORE.Window.screen.height *= scaleDpi.y; + } +#endif - // we update the window position right away - CORE.Window.position.x = CORE.Window.previousPosition.x; - CORE.Window.position.y = CORE.Window.previousPosition.y; + glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, + CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) @@ -224,13 +239,8 @@ void ToggleFullscreen(void) void ToggleBorderlessWindowed(void) { // Leave fullscreen before attempting to set borderless windowed mode - bool wasOnFullscreen = false; - if (CORE.Window.fullscreen) - { - // Fullscreen already saves the previous position so it does not need to be set here again - ToggleFullscreen(); - wasOnFullscreen = true; - } + // NOTE: Fullscreen already saves the previous position so it does not need to be set again later + if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) ToggleFullscreen(); int monitorCount = 0; GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); @@ -246,7 +256,7 @@ void ToggleBorderlessWindowed(void) { // Store screen position and size // NOTE: If it was on fullscreen, screen position was already stored, so skip setting it here - if (!wasOnFullscreen) CORE.Window.previousPosition = CORE.Window.position; + CORE.Window.previousPosition = CORE.Window.position; CORE.Window.previousScreen = CORE.Window.screen; // Set undecorated flag @@ -261,15 +271,8 @@ void ToggleBorderlessWindowed(void) const int monitorHeight = mode->height; // Set screen position and size - glfwSetWindowMonitor( - platform.handle, - monitors[monitor], - monitorPosX, - monitorPosY, - monitorWidth, - monitorHeight, - mode->refreshRate - ); + glfwSetWindowMonitor(platform.handle, monitors[monitor], monitorPosX, monitorPosY, + monitorWidth, monitorHeight, mode->refreshRate); // Refocus window glfwFocusWindow(platform.handle); @@ -278,39 +281,32 @@ void ToggleBorderlessWindowed(void) } else { + // Restore previous screen values + CORE.Window.position = CORE.Window.previousPosition; + CORE.Window.screen = CORE.Window.previousScreen; + // Remove undecorated flag glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE); FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNDECORATED); #if !defined(__APPLE__) - // Make sure to restore size to HighDPI + // Make sure to restore size considering HighDPI scaling if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) { Vector2 scaleDpi = GetWindowScaleDPI(); - CORE.Window.previousScreen.width *= scaleDpi.x; - CORE.Window.previousScreen.height *= scaleDpi.y; + CORE.Window.screen.width *= scaleDpi.x; + CORE.Window.screen.height *= scaleDpi.y; } #endif - // Return previous screen size and position - // NOTE: The order matters here, it must set size first, then set position, otherwise the screen will be positioned incorrectly - glfwSetWindowMonitor( - platform.handle, - NULL, - CORE.Window.previousPosition.x, - CORE.Window.previousPosition.y, - CORE.Window.previousScreen.width, - CORE.Window.previousScreen.height, - mode->refreshRate - ); + // Return to previous screen size and position + glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, + CORE.Window.screen.width, CORE.Window.screen.height, mode->refreshRate); // Refocus window glfwFocusWindow(platform.handle); FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE); - - CORE.Window.position.x = CORE.Window.previousPosition.x; - CORE.Window.position.y = CORE.Window.previousPosition.y; } } else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); @@ -1023,7 +1019,8 @@ Vector2 GetWindowPosition(void) Vector2 GetWindowScaleDPI(void) { Vector2 scale = { 1.0f, 1.0f }; - if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) glfwGetWindowContentScale(platform.handle, &scale.x, &scale.y); + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI) && !FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) + glfwGetWindowContentScale(platform.handle, &scale.x, &scale.y); return scale; } @@ -1553,59 +1550,20 @@ int InitPlatform(void) CORE.Window.display.width = mode->width; CORE.Window.display.height = mode->height; - // Set screen width/height to the display width/height if they are 0 + // Check if user requested some screen size + if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0)) + { + // Set some default screen size in case user decides to exit fullscreen mode + CORE.Window.previousScreen.width = 800; + CORE.Window.previousScreen.height = 450; + CORE.Window.previousPosition.x = CORE.Window.display.width/2 - 800/2; + CORE.Window.previousPosition.y = CORE.Window.display.height/2 - 450/2; + } + + // Set screen width/height to the display width/height if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width; if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height; - // Remember center for switching from fullscreen to window - if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) - { - // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed - // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11 - CORE.Window.position.x = CORE.Window.display.width/4; - CORE.Window.position.y = CORE.Window.display.height/4; - } - else - { - CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2; - CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2; - } - - if (CORE.Window.position.x < 0) CORE.Window.position.x = 0; - if (CORE.Window.position.y < 0) CORE.Window.position.y = 0; - - // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor - int count = 0; - const GLFWvidmode *modes = glfwGetVideoModes(monitor, &count); - - // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height - for (int i = 0; i < count; i++) - { - if ((unsigned int)modes[i].width >= CORE.Window.screen.width) - { - if ((unsigned int)modes[i].height >= CORE.Window.screen.height) - { - CORE.Window.display.width = modes[i].width; - CORE.Window.display.height = modes[i].height; - break; - } - } - } - - TRACELOG(LOG_INFO, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - - // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example, - // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3), - // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched - // by the sides to fit all monitor space... - - // Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight - // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset) - // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale - // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed... - // HighDPI monitors are properly considered in a following similar function: SetupViewport() - SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", monitor, NULL); if (!platform.handle) { @@ -1616,14 +1574,11 @@ int InitPlatform(void) } else { - // No-fullscreen window creation - bool requestWindowedFullscreen = (CORE.Window.screen.height == 0) && (CORE.Window.screen.width == 0); - // Default to at least one pixel in size, as creation with a zero dimension is not allowed - int creationWidth = (CORE.Window.screen.width != 0)? CORE.Window.screen.width : 1; - int creationHeight = (CORE.Window.screen.height != 0)? CORE.Window.screen.height : 1; + if (CORE.Window.screen.width == 0) CORE.Window.screen.width = 1; + if (CORE.Window.screen.height == 0) CORE.Window.screen.height = 1; - platform.handle = glfwCreateWindow(creationWidth, creationHeight, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); + platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); if (!platform.handle) { glfwTerminate(); @@ -1632,7 +1587,7 @@ int InitPlatform(void) } // After the window was created, determine the monitor that the window manager assigned - // Derive display sizes, and, if possible, window size in case it was zero at beginning + // Derive display sizes and, if possible, window size in case it was zero at beginning int monitorCount = 0; int monitorIndex = GetCurrentMonitor(); @@ -1640,7 +1595,7 @@ int InitPlatform(void) if (monitorIndex < monitorCount) { - monitor = monitors[monitorIndex]; + GLFWmonitor *monitor = monitors[monitorIndex]; const GLFWvidmode *mode = glfwGetVideoMode(monitor); // Default display resolution to that of the current mode @@ -1651,7 +1606,7 @@ int InitPlatform(void) if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width; if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height; - if (requestWindowedFullscreen) glfwSetWindowSize(platform.handle, CORE.Window.screen.width, CORE.Window.screen.height); + glfwSetWindowSize(platform.handle, CORE.Window.screen.width, CORE.Window.screen.height); } else { @@ -1693,6 +1648,8 @@ int InitPlatform(void) { // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling // Framebuffer scaling is activated with: glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE); + + // Get current framebuffer size, on high-dpi it could be bigger than screen size glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight); // Screen scaling matrix is required in case desired screen area is different from display area @@ -1726,6 +1683,11 @@ int InitPlatform(void) if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else { + int monitorCount = 0; + int monitorIndex = GetCurrentMonitor(); + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + GLFWmonitor *monitor = monitors[monitorIndex]; + // Try to center window on screen but avoiding window-bar outside of screen int monitorX = 0; int monitorY = 0; @@ -1733,7 +1695,7 @@ int InitPlatform(void) int monitorHeight = 0; glfwGetMonitorWorkarea(monitor, &monitorX, &monitorY, &monitorWidth, &monitorHeight); - // Here CORE.Window.render.width/height should be used instead of + // TODO: Here CORE.Window.render.width/height should be used instead of // CORE.Window.screen.width/height to center the window correctly when the high dpi flag is enabled int posX = monitorX + (monitorWidth - (int)CORE.Window.render.width)/2; int posY = monitorY + (monitorHeight - (int)CORE.Window.render.height)/2; @@ -1855,7 +1817,7 @@ static void WindowSizeCallback(GLFWwindow *window, int width, int height) // WARNING: If FLAG_WINDOW_HIGHDPI is set, WindowContentScaleCallback() is called before this function static void FramebufferSizeCallback(GLFWwindow *window, int width, int height) { - //TRACELOG(LOG_INFO, "GLFW3: Window framebuffer size callback called [%i,%i]", width, height); + TRACELOG(LOG_INFO, "GLFW3: Window framebuffer size callback called [%i,%i]", width, height); // WARNING: On window minimization, callback is called, // but we don't want to change internal screen values, it breaks things @@ -1870,19 +1832,38 @@ static void FramebufferSizeCallback(GLFWwindow *window, int width, int height) CORE.Window.currentFbo.height = height; CORE.Window.resizedLastFrame = true; - // Check if render size was actually scaled for high-dpi - if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) - { - // Set screen size to logical pixel size, considering content scaling - Vector2 scaleDpi = GetWindowScaleDPI(); - CORE.Window.screen.width = (int)((float)width/scaleDpi.x); - CORE.Window.screen.height = (int)((float)height/scaleDpi.y); - } - else + if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) { + // On fullscreen mode, strategy is ignoring high-dpi and + // use the all available display size + // Set screen size to render size (physical pixel size) CORE.Window.screen.width = width; CORE.Window.screen.height = height; + CORE.Window.screenScale = MatrixScale(1.0f, 1.0f, 1.0f); + SetMouseScale(1.0f, 1.0f); + } + else // Window mode (including borderless window) + { + // Check if render size was actually scaled for high-dpi + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) + { + // Set screen size to logical pixel size, considering content scaling + Vector2 scaleDpi = GetWindowScaleDPI(); + CORE.Window.screen.width = (int)((float)width/scaleDpi.x); + CORE.Window.screen.height = (int)((float)height/scaleDpi.y); + CORE.Window.screenScale = MatrixScale(scaleDpi.x, scaleDpi.y, 1.0f); +#if !defined(__APPLE__) + // Mouse input scaling for the new screen size + SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y); +#endif + } + else + { + // Set screen size to render size (physical pixel size) + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + } } // WARNING: If using a render texture, it is not scaled to new size @@ -1903,13 +1884,12 @@ static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float s #if !defined(__APPLE__) // Mouse input scaling for the new screen size - SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight); + SetMouseScale(1.0f/scalex, 1.0f/scaley); #endif CORE.Window.render.width = (int)fbWidth; CORE.Window.render.height = (int)fbHeight; - CORE.Window.currentFbo.width = (int)fbWidth; - CORE.Window.currentFbo.height = (int)fbHeight; + CORE.Window.currentFbo = CORE.Window.render; } // GLFW3: Window position callback, runs when window position changes From 11c248aa820ffd35a8684ad68b521fd101095418 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Dec 2025 16:20:43 +0100 Subject: [PATCH 025/117] Update rcore_web.c --- src/platforms/rcore_web.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index e138de302..b52ca1bf0 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -1290,18 +1290,6 @@ int InitPlatform(void) TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example, - // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3), - // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched - // by the sides to fit all monitor space... - - // Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight - // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset) - // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale - // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed... - // HighDPI monitors are properly considered in a following similar function: SetupViewport() - SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); // NOTE: Full-screen change, not working properly... From c0c8ee9dc8240e2567aed5e1b37f8d9261f400ea Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Dec 2025 18:15:47 +0100 Subject: [PATCH 026/117] Update rcore_desktop_glfw.c --- src/platforms/rcore_desktop_glfw.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 9b360771f..ef5f5ed4f 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -267,12 +267,15 @@ void ToggleBorderlessWindowed(void) int monitorPosX = 0; int monitorPosY = 0; glfwGetMonitorPos(monitors[monitor], &monitorPosX, &monitorPosY); - const int monitorWidth = mode->width; - const int monitorHeight = mode->height; + CORE.Window.position.x = monitorPosX; + CORE.Window.position.x = monitorPosY; + + CORE.Window.screen.width = mode->width; + CORE.Window.screen.height = mode->height; // Set screen position and size - glfwSetWindowMonitor(platform.handle, monitors[monitor], monitorPosX, monitorPosY, - monitorWidth, monitorHeight, mode->refreshRate); + glfwSetWindowMonitor(platform.handle, monitors[monitor], CORE.Window.position.x, CORE.Window.position.y, + CORE.Window.screen.width, CORE.Window.screen.height, mode->refreshRate); // Refocus window glfwFocusWindow(platform.handle); @@ -1817,7 +1820,7 @@ static void WindowSizeCallback(GLFWwindow *window, int width, int height) // WARNING: If FLAG_WINDOW_HIGHDPI is set, WindowContentScaleCallback() is called before this function static void FramebufferSizeCallback(GLFWwindow *window, int width, int height) { - TRACELOG(LOG_INFO, "GLFW3: Window framebuffer size callback called [%i,%i]", width, height); + //TRACELOG(LOG_INFO, "GLFW3: Window framebuffer size callback called [%i,%i]", width, height); // WARNING: On window minimization, callback is called, // but we don't want to change internal screen values, it breaks things @@ -1873,7 +1876,7 @@ static void FramebufferSizeCallback(GLFWwindow *window, int width, int height) // WARNING: If FLAG_WINDOW_HIGHDPI is not set, this function is not called static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley) { - TRACELOG(LOG_INFO, "GLFW3: Window content scale changed, scale: [%.2f,%.2f]", scalex, scaley); + //TRACELOG(LOG_INFO, "GLFW3: Window content scale changed, scale: [%.2f,%.2f]", scalex, scaley); float fbWidth = (float)CORE.Window.screen.width*scalex; float fbHeight = (float)CORE.Window.screen.height*scaley; From 4176c518c74b3571bef7113dbb90a4a656e3012f Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Dec 2025 18:40:44 +0100 Subject: [PATCH 027/117] Update rcore_desktop_glfw.c --- src/platforms/rcore_desktop_glfw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index ef5f5ed4f..f5b3711c5 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -268,7 +268,7 @@ void ToggleBorderlessWindowed(void) int monitorPosY = 0; glfwGetMonitorPos(monitors[monitor], &monitorPosX, &monitorPosY); CORE.Window.position.x = monitorPosX; - CORE.Window.position.x = monitorPosY; + CORE.Window.position.y = monitorPosY; CORE.Window.screen.width = mode->width; CORE.Window.screen.height = mode->height; From 8871d7648d6edb2fb3de70f5feba613df84ecf92 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Dec 2025 19:49:38 +0100 Subject: [PATCH 028/117] Update core_highdpi_testbed.c --- examples/core/core_highdpi_testbed.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/core/core_highdpi_testbed.c b/examples/core/core_highdpi_testbed.c index d34951fc0..8142a1a1b 100644 --- a/examples/core/core_highdpi_testbed.c +++ b/examples/core/core_highdpi_testbed.c @@ -33,6 +33,7 @@ int main(void) Vector2 scaleDpi = GetWindowScaleDPI(); Vector2 mousePos = GetMousePosition(); int currentMonitor = GetCurrentMonitor(); + Vector2 windowPos = GetWindowPosition(); int gridSpacing = 40; // Grid spacing in pixels @@ -47,6 +48,7 @@ int main(void) mousePos = GetMousePosition(); currentMonitor = GetCurrentMonitor(); scaleDpi = GetWindowScaleDPI(); + windowPos = GetWindowPosition(); if (IsKeyPressed(KEY_SPACE)) ToggleBorderlessWindowed(); if (IsKeyPressed(KEY_F)) ToggleFullscreen(); @@ -73,9 +75,10 @@ int main(void) // Draw UI info DrawText(TextFormat("CURRENT MONITOR: %i/%i (%ix%i)", currentMonitor + 1, GetMonitorCount(), GetMonitorWidth(currentMonitor), GetMonitorHeight(currentMonitor)), 50, 50, 20, DARKGRAY); - DrawText(TextFormat("SCREEN SIZE: %ix%i", GetScreenWidth(), GetScreenHeight()), 50, 90, 20, DARKGRAY); - DrawText(TextFormat("RENDER SIZE: %ix%i", GetRenderWidth(), GetRenderHeight()), 50, 130, 20, DARKGRAY); - DrawText(TextFormat("SCALE FACTOR: %.1fx%.1f", scaleDpi.x, scaleDpi.y), 50, 170, 20, GRAY); + DrawText(TextFormat("WINDOW POSITION: %ix%i", windowPos.x, windowPos.y), 50, 90, 20, DARKGRAY); + DrawText(TextFormat("SCREEN SIZE: %ix%i", GetScreenWidth(), GetScreenHeight()), 50, 130, 20, DARKGRAY); + DrawText(TextFormat("RENDER SIZE: %ix%i", GetRenderWidth(), GetRenderHeight()), 50, 170, 20, DARKGRAY); + DrawText(TextFormat("SCALE FACTOR: %.1fx%.1f", scaleDpi.x, scaleDpi.y), 50, 210, 20, GRAY); // Draw reference rectangles, top-left and bottom-right corners DrawRectangle(0, 0, 30, 60, RED); From 8a75439c255088a0cc4c12becb42de57900ade08 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Dec 2025 19:51:04 +0100 Subject: [PATCH 029/117] REVIEWED: Fullscreen modes on Linux (X11 over XWayland) It does not work as expected... :( --- src/platforms/rcore_desktop_glfw.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index f5b3711c5..88120be78 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -196,11 +196,16 @@ void ToggleFullscreen(void) CORE.Window.display.height = mode->height; CORE.Window.position = (Point){ 0, 0 }; - CORE.Window.screen = (Size){ CORE.Window.display.width, CORE.Window.display.height }; + CORE.Window.screen = CORE.Window.display; // Set fullscreen flag to be processed on FramebufferSizeCallback() accordingly FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); + // NOTE: X11 requires undecorating the window before switching to + // fullscreen to avoid issues with framebuffer scaling + glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE); + FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED); + // WARNING: This function launches FramebufferSizeCallback() glfwSetWindowMonitor(platform.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } @@ -226,8 +231,14 @@ void ToggleFullscreen(void) } #endif + // WARNING: This function launches FramebufferSizeCallback() glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + + // NOTE: X11 requires restoring the decorated window after switching from + // fullscreen to avoid issues with framebuffer scaling + glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE); + FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNDECORATED); } // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) From 6450a48c750862aa40a24b34187b98dff87aabd2 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Dec 2025 20:03:51 +0100 Subject: [PATCH 030/117] Update core_highdpi_testbed.c --- examples/core/core_highdpi_testbed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_highdpi_testbed.c b/examples/core/core_highdpi_testbed.c index 8142a1a1b..a925527db 100644 --- a/examples/core/core_highdpi_testbed.c +++ b/examples/core/core_highdpi_testbed.c @@ -75,7 +75,7 @@ int main(void) // Draw UI info DrawText(TextFormat("CURRENT MONITOR: %i/%i (%ix%i)", currentMonitor + 1, GetMonitorCount(), GetMonitorWidth(currentMonitor), GetMonitorHeight(currentMonitor)), 50, 50, 20, DARKGRAY); - DrawText(TextFormat("WINDOW POSITION: %ix%i", windowPos.x, windowPos.y), 50, 90, 20, DARKGRAY); + DrawText(TextFormat("WINDOW POSITION: %ix%i", (int)windowPos.x, (int)windowPos.y), 50, 90, 20, DARKGRAY); DrawText(TextFormat("SCREEN SIZE: %ix%i", GetScreenWidth(), GetScreenHeight()), 50, 130, 20, DARKGRAY); DrawText(TextFormat("RENDER SIZE: %ix%i", GetRenderWidth(), GetRenderHeight()), 50, 170, 20, DARKGRAY); DrawText(TextFormat("SCALE FACTOR: %.1fx%.1f", scaleDpi.x, scaleDpi.y), 50, 210, 20, GRAY); From 890ca8d6870585f9e3499eb0e7a327af2cabaa1b Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Dec 2025 20:04:44 +0100 Subject: [PATCH 031/117] REVIEWED: `GetWindowPosition()`, return internal value --- src/platforms/rcore_desktop_glfw.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 88120be78..1593f0306 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1021,12 +1021,7 @@ const char *GetMonitorName(int monitor) // Get window position XY on monitor Vector2 GetWindowPosition(void) { - int x = 0; - int y = 0; - - glfwGetWindowPos(platform.handle, &x, &y); - - return (Vector2){ (float)x, (float)y }; + return (Vector2){ (float)CORE.Window.position.x, (float)CORE.Window.position.y }; } // Get window scale DPI factor for current monitor From a334a54eacc90b93ce68e1bcb092ce1d08088a93 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Dec 2025 20:09:15 +0100 Subject: [PATCH 032/117] Update rcore_desktop_glfw.c --- src/platforms/rcore_desktop_glfw.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 1593f0306..1741187e4 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -275,12 +275,7 @@ void ToggleBorderlessWindowed(void) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED); // Get monitor position and size - int monitorPosX = 0; - int monitorPosY = 0; - glfwGetMonitorPos(monitors[monitor], &monitorPosX, &monitorPosY); - CORE.Window.position.x = monitorPosX; - CORE.Window.position.y = monitorPosY; - + glfwGetMonitorPos(monitors[monitor], &CORE.Window.position.x, &CORE.Window.position.y); CORE.Window.screen.width = mode->width; CORE.Window.screen.height = mode->height; From eb3cc183ccce0670054c3accad61e759515bbe58 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Dec 2025 20:29:01 +0100 Subject: [PATCH 033/117] REVIEWED: FIXED: Windows fullscreen, after breaking it due to X11/Wayland changes --- src/platforms/rcore_desktop_glfw.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 1741187e4..42e8f3867 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -201,11 +201,12 @@ void ToggleFullscreen(void) // Set fullscreen flag to be processed on FramebufferSizeCallback() accordingly FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE); +#if defined(_GLFW_X11) || defined(_GLFW_WAYLAND) // NOTE: X11 requires undecorating the window before switching to // fullscreen to avoid issues with framebuffer scaling glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE); FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED); - +#endif // WARNING: This function launches FramebufferSizeCallback() glfwSetWindowMonitor(platform.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } @@ -235,10 +236,12 @@ void ToggleFullscreen(void) glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); +#if defined(_GLFW_X11) || defined(_GLFW_WAYLAND) // NOTE: X11 requires restoring the decorated window after switching from // fullscreen to avoid issues with framebuffer scaling glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE); FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNDECORATED); +#endif } // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) From 8f8346048ce12596094dbd4f63c004fb1d5587e5 Mon Sep 17 00:00:00 2001 From: Meowster <142757105+meowstr@users.noreply.github.com> Date: Sun, 28 Dec 2025 17:50:10 -0500 Subject: [PATCH 034/117] Add chicken scheme to BINDINGS.md (#5449) * Add chicken scheme to BINDINGS.md * Fix typo --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index ee7da46f4..37274b3be 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -21,6 +21,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers | [claw-raylib](https://github.com/bohonghuang/claw-raylib) | **auto** | [Common Lisp](https://common-lisp.net) | Apache-2.0 | | [raylib](https://github.com/fosskers/raylib) | 5.5 | [Common Lisp](https://common-lisp.net) | MPL-2.0 | | [chez-raylib](https://github.com/Yunoinsky/chez-raylib) | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme) | GPLv3 | +| [chicken-raylib](https://github.com/meowstr/chicken-raylib) | 5.5 | [CHICKEN Scheme](https://wiki.call-cc.org) | MIT | | [CLIPSraylib](https://github.com/mrryanjohnston/CLIPSraylib) | **auto** | [CLIPS](https://www.clipsrules.net/) | MIT | | [raylib-cr](https://github.com/sol-vin/raylib-cr) | 4.6-dev (5e1a81) | [Crystal](https://crystal-lang.org) | Apache-2.0 | | [ray-cyber](https://github.com/fubark/ray-cyber) | **5.0** | [Cyber](https://cyberscript.dev) | MIT | From 58d414bcf878bfa11c6afc871e9d6444d0f0e307 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 29 Dec 2025 12:39:40 +0100 Subject: [PATCH 035/117] REVIEWED: `InitPlatform()`, code simplification --- src/platforms/rcore_desktop_glfw.c | 57 ++++++++++++++---------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 42e8f3867..a1c0024aa 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1565,13 +1565,18 @@ int InitPlatform(void) CORE.Window.previousScreen.height = 450; CORE.Window.previousPosition.x = CORE.Window.display.width/2 - 800/2; CORE.Window.previousPosition.y = CORE.Window.display.height/2 - 450/2; + + // Set screen width/height to the display width/height + if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width; + if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height; + } + else + { + CORE.Window.previousScreen = CORE.Window.screen; + CORE.Window.screen = CORE.Window.display; } - // Set screen width/height to the display width/height - if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width; - if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height; - - platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", monitor, NULL); + platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", monitor, NULL); if (!platform.handle) { glfwTerminate(); @@ -1630,13 +1635,13 @@ int InitPlatform(void) glfwMakeContextCurrent(platform.handle); result = glfwGetError(NULL); + if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) CORE.Window.ready = true; // Checking context activation - // Check context activation - if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) + if (CORE.Window.ready) { - CORE.Window.ready = true; + // Setup additional windows configs and register required window size info - glfwSwapInterval(0); // No V-Sync by default + glfwSwapInterval(0); // No V-Sync by default // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need @@ -1677,25 +1682,13 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - } - else - { - TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); - return -1; - } - if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow(); - - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } - else - { + // Try to center window on screen but avoiding window-bar outside of screen int monitorCount = 0; int monitorIndex = GetCurrentMonitor(); GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); GLFWmonitor *monitor = monitors[monitorIndex]; - // Try to center window on screen but avoiding window-bar outside of screen int monitorX = 0; int monitorY = 0; int monitorWidth = 0; @@ -1704,15 +1697,19 @@ int InitPlatform(void) // TODO: Here CORE.Window.render.width/height should be used instead of // CORE.Window.screen.width/height to center the window correctly when the high dpi flag is enabled - int posX = monitorX + (monitorWidth - (int)CORE.Window.render.width)/2; - int posY = monitorY + (monitorHeight - (int)CORE.Window.render.height)/2; - if (posX < monitorX) posX = monitorX; - if (posY < monitorY) posY = monitorY; - SetWindowPosition(posX, posY); + CORE.Window.position.x = monitorX + (monitorWidth - (int)CORE.Window.screen.width)/2; + CORE.Window.position.y = monitorY + (monitorHeight - (int)CORE.Window.screen.height)/2; + //if (CORE.Window.position.x < monitorX) CORE.Window.position.x = monitorX; + //if (CORE.Window.position.y < monitorY) CORE.Window.position.y = monitorY; - // Update CORE.Window.position here so it is correct from the start - CORE.Window.position.x = posX; - CORE.Window.position.y = posY; + SetWindowPosition(CORE.Window.position.x, CORE.Window.position.y); + + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow(); + } + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + return -1; } // Apply window flags requested previous to initialization From 00f42e419913c0165d27b74eaa6369717c89540c Mon Sep 17 00:00:00 2001 From: Padmadev D <128023777+padmadevd@users.noreply.github.com> Date: Mon, 29 Dec 2025 17:20:12 +0530 Subject: [PATCH 036/117] [rcore] [android] fixed gesture system not reporting GESTURE_NONE (#5452) in android gesture system is not reporting GESTURE_NONE, specified in the issue https://github.com/raysan5/raylib/issues/5010 so, automatically GESTURE_SWIPE, TAP, DOUBLE_TAP, also will not be reported. in this commit it is fixed. --- src/platforms/rcore_android.c | 106 +++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index f150bf638..cc012c4fe 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -1336,30 +1336,17 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) } } - if ((flags == AMOTION_EVENT_ACTION_POINTER_UP) || (flags == AMOTION_EVENT_ACTION_UP) || (flags == AMOTION_EVENT_ACTION_HOVER_EXIT)) - { - // One of the touchpoints is released, remove it from touch point arrays - if (flags == AMOTION_EVENT_ACTION_HOVER_EXIT) - { - // If the touchPoint is hover, remove it from hoverPoints - for (int i = 0; i < MAX_TOUCH_POINTS; i++) - { - if (touchRaw.hoverPoints[i] == touchRaw.pointId[pointerIndex]) - { - touchRaw.hoverPoints[i] = -1; - break; - } - } - } - for (int i = pointerIndex; (i < touchRaw.pointCount - 1) && (i < MAX_TOUCH_POINTS - 1); i++) - { - touchRaw.pointId[i] = touchRaw.pointId[i+1]; - touchRaw.position[i] = touchRaw.position[i+1]; - } - touchRaw.pointCount--; - } +#if defined(SUPPORT_GESTURES_SYSTEM) + GestureEvent gestureEvent = { 0 }; + + gestureEvent.pointCount = 0; + + // Register touch actions + if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_ACTION_DOWN; + else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.touchAction = TOUCH_ACTION_UP; + else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE; + else if (flags == AMOTION_EVENT_ACTION_CANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL; - int pointCount = 0; for (int i = 0; (i < touchRaw.pointCount) && (i < MAX_TOUCH_POINTS); i++) { // If the touchPoint is hover, Ignore it @@ -1375,35 +1362,62 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) } if (hover) continue; - CORE.Input.Touch.pointId[pointCount] = touchRaw.pointId[i]; - CORE.Input.Touch.position[pointCount] = touchRaw.position[i]; - pointCount++; - } - CORE.Input.Touch.pointCount = pointCount; - -#if defined(SUPPORT_GESTURES_SYSTEM) - GestureEvent gestureEvent = { 0 }; - - gestureEvent.pointCount = CORE.Input.Touch.pointCount; - - // Register touch actions - if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_ACTION_DOWN; - else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.touchAction = TOUCH_ACTION_UP; - else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE; - else if (flags == AMOTION_EVENT_ACTION_CANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL; - - for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++) - { - gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i]; - gestureEvent.position[i] = CORE.Input.Touch.position[i]; - gestureEvent.position[i].x /= (float)GetScreenWidth(); - gestureEvent.position[i].y /= (float)GetScreenHeight(); + gestureEvent.pointId[gestureEvent.pointCount] = touchRaw.pointId[i]; + gestureEvent.position[gestureEvent.pointCount] = touchRaw.position[i]; + gestureEvent.position[gestureEvent.pointCount].x /= (float)GetScreenWidth(); + gestureEvent.position[gestureEvent.pointCount].y /= (float)GetScreenHeight(); + gestureEvent.pointCount++; } // Gesture data is sent to gestures system for processing ProcessGestureEvent(gestureEvent); #endif + if (flags == AMOTION_EVENT_ACTION_HOVER_EXIT) + { + // Hover exited. So, remove it from hoverPoints + for (int i = 0; i < MAX_TOUCH_POINTS; i++) + { + if (touchRaw.hoverPoints[i] == touchRaw.pointId[pointerIndex]) + { + touchRaw.hoverPoints[i] = -1; + break; + } + } + } + + if ((flags == AMOTION_EVENT_ACTION_POINTER_UP) || (flags == AMOTION_EVENT_ACTION_UP)) + { + // One of the touchpoints is released, remove it from touch point arrays + for (int i = pointerIndex; (i < touchRaw.pointCount - 1) && (i < MAX_TOUCH_POINTS - 1); i++) + { + touchRaw.pointId[i] = touchRaw.pointId[i+1]; + touchRaw.position[i] = touchRaw.position[i+1]; + } + touchRaw.pointCount--; + } + + CORE.Input.Touch.pointCount = 0; + for (int i = 0; (i < touchRaw.pointCount) && (i < MAX_TOUCH_POINTS); i++) + { + // If the touchPoint is hover, Ignore it + bool hover = false; + for (int j = 0; j < MAX_TOUCH_POINTS; j++) + { + // Check if the touchPoint is in hoverPointers + if (touchRaw.hoverPoints[j] == touchRaw.pointId[i]) + { + hover = true; + break; + } + } + if (hover) continue; + + CORE.Input.Touch.pointId[CORE.Input.Touch.pointCount] = touchRaw.pointId[i]; + CORE.Input.Touch.position[CORE.Input.Touch.pointCount] = touchRaw.position[i]; + CORE.Input.Touch.pointCount++; + } + // When all touchpoints are tapped and released really quickly, this event is generated if (flags == AMOTION_EVENT_ACTION_CANCEL) CORE.Input.Touch.pointCount = 0; From 1c6f6831613163d3724e15b322f45a458a011354 Mon Sep 17 00:00:00 2001 From: MULTi <78434796+MULTidll@users.noreply.github.com> Date: Mon, 29 Dec 2025 17:24:30 +0530 Subject: [PATCH 037/117] [rcore][drm] Improved touch input handling and multitouch support, closes #4842 (#5447) * Improved touch input handling and multitouch support in drm platform * revert * made some fixes for the touch issue in drm platform * updated touch input handling by adding multitouch support * improved how it handles the multitouch * added cleanup * Remove touch last update tracking to simplify touch input handling * improved multitouch support by tracking touch positions and IDs for each slot * Better touch input handling * Increase maximum touch points from 8 to 10 and enhance touchscreen prioritization logic * Refactor touch input handling to use slot index as ID for stability and simplify touch clearing logic * Improve touch input handling by activating slot 0 based on mouse click or touch events * touch event handling to use tracking ID for unique touch identification * Add multitouch detection to PollMouseEvents for improved touch handling * Fix conditional formatting in PollMouseEvents for clarity * Refactor conditional statements in PollMouseEvents and InitPlatform for improved readability * Fix formatting in PollMouseEvents for improved readability --- src/config.h | 2 +- src/platforms/rcore_drm.c | 253 +++++++++++++++++++++++++++++++------- 2 files changed, 212 insertions(+), 43 deletions(-) diff --git a/src/config.h b/src/config.h index b749f8952..9f54edd23 100644 --- a/src/config.h +++ b/src/config.h @@ -105,7 +105,7 @@ #define MAX_GAMEPAD_AXES 8 // Maximum number of axes supported (per gamepad) #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad) #define MAX_GAMEPAD_VIBRATION_TIME 2.0f // Maximum vibration time in seconds -#define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported +#define MAX_TOUCH_POINTS 10 // 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 diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 366477aac..b97eac5f5 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -135,8 +135,12 @@ typedef struct { char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab bool cursorRelative; // Relative cursor mode int mouseFd; // File descriptor for the evdev mouse/touch/gestures + bool mouseIsTouch; // Check if the current mouse device is actually a touchscreen Rectangle absRange; // Range of values for absolute pointing devices (touchscreens) int touchSlot; // Hold the touch slot number of the currently being sent multitouch block + bool touchActive[MAX_TOUCH_POINTS]; // Track which touch points are currently active + Vector2 touchPosition[MAX_TOUCH_POINTS]; // Track touch positions for each slot + int touchId[MAX_TOUCH_POINTS]; // Track touch IDs for each slot // Gamepad data int gamepadStreamFd[MAX_GAMEPADS]; // Gamepad device file descriptor @@ -1115,9 +1119,6 @@ void PollInputEvents(void) // Register previous touch states for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; - // Reset touch positions to invalid state - for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ -1, -1 }; - // Map touch position to mouse position for convenience // NOTE: For DRM touchscreen devices, this mapping is disabled to avoid false touch detection // CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; @@ -1565,7 +1566,11 @@ int InitPlatform(void) if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow(); // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } + if (!CORE.Window.ready) + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); + return -1; + } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); // Set some default window flags @@ -1883,7 +1888,14 @@ static void InitEvdevInput(void) { CORE.Input.Touch.position[i].x = -1; CORE.Input.Touch.position[i].y = -1; + platform.touchActive[i] = false; + platform.touchPosition[i].x = -1; + platform.touchPosition[i].y = -1; + platform.touchId[i] = -1; } + + // Initialize touch slot + platform.touchSlot = 0; // Reset keyboard key state for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) @@ -2047,17 +2059,49 @@ static void ConfigureEvdevDevice(char *device) const char *deviceKindStr = "unknown"; if (isMouse || isTouch) { - deviceKindStr = "mouse"; - if (platform.mouseFd != -1) close(platform.mouseFd); - platform.mouseFd = fd; + bool prioritize = false; - if (absAxisCount > 0) + // Priority logic: Touchscreens override Mice. + // 1. No device set yet? Take it. + if (platform.mouseFd == -1) prioritize = true; + // 2. Current is Mouse, New is Touch? Upgrade to Touch. + else if (isTouch && !platform.mouseIsTouch) prioritize = true; + // 3. Current is Touch, New is Touch? Use the new one (Last one found wins, standard behavior). + else if (isTouch && platform.mouseIsTouch) prioritize = true; + // 4. Current is Mouse, New is Mouse? Use the new one. + else if (!isTouch && !platform.mouseIsTouch) prioritize = true; + // 5. Current is Touch, New is Mouse? IGNORE the mouse. Keep the touchscreen. + else prioritize = false; + + if (prioritize) { - platform.absRange.x = absinfo[ABS_X].info.minimum; - platform.absRange.width = absinfo[ABS_X].info.maximum - absinfo[ABS_X].info.minimum; + deviceKindStr = isTouch ? "touchscreen" : "mouse"; + + if (platform.mouseFd != -1) + { + TRACELOG(LOG_INFO, "INPUT: Overwriting previous input device with new %s", deviceKindStr); + close(platform.mouseFd); + } + + platform.mouseFd = fd; + platform.mouseIsTouch = isTouch; - platform.absRange.y = absinfo[ABS_Y].info.minimum; - platform.absRange.height = absinfo[ABS_Y].info.maximum - absinfo[ABS_Y].info.minimum; + if (absAxisCount > 0) + { + platform.absRange.x = absinfo[ABS_X].info.minimum; + platform.absRange.width = absinfo[ABS_X].info.maximum - absinfo[ABS_X].info.minimum; + + platform.absRange.y = absinfo[ABS_Y].info.minimum; + platform.absRange.height = absinfo[ABS_Y].info.maximum - absinfo[ABS_Y].info.minimum; + } + + TRACELOG(LOG_INFO, "INPUT: Initialized input device %s as %s", device, deviceKindStr); + } + else + { + TRACELOG(LOG_INFO, "INPUT: Ignoring device %s (keeping higher priority %s device)", device, platform.mouseIsTouch ? "touchscreen" : "mouse"); + close(fd); + return; } } else if (isGamepad && !isMouse && !isKeyboard && (platform.gamepadCount < MAX_GAMEPADS)) @@ -2231,6 +2275,7 @@ static void PollMouseEvents(void) struct input_event event = { 0 }; int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE + static bool isMultitouch = false; // Detect if device supports MT events // Try to read data from the mouse/touch/gesture and only continue if successful while (read(fd, &event, sizeof(event)) == (int)sizeof(event)) @@ -2276,54 +2321,118 @@ static void PollMouseEvents(void) if (event.code == ABS_X) { CORE.Input.Mouse.currentPosition.x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; // Scale according to absRange - CORE.Input.Touch.position[0].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; // Scale according to absRange - - touchAction = 2; // TOUCH_ACTION_MOVE + + // Update single touch position only if it's active and no MT events are being used + if ((platform.touchActive[0]) && (!isMultitouch)) + { + platform.touchPosition[0].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; + if (touchAction == -1) touchAction = 2; // TOUCH_ACTION_MOVE + } } if (event.code == ABS_Y) { CORE.Input.Mouse.currentPosition.y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; // Scale according to absRange - CORE.Input.Touch.position[0].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; // Scale according to absRange - - touchAction = 2; // TOUCH_ACTION_MOVE + + // Update single touch position only if it's active and no MT events are being used + if ((platform.touchActive[0]) && (!isMultitouch)) + { + platform.touchPosition[0].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; + if (touchAction == -1) touchAction = 2; // TOUCH_ACTION_MOVE + } } // Multitouch movement - if (event.code == ABS_MT_SLOT) platform.touchSlot = event.value; // Remember the slot number for the folowing events - - if (event.code == ABS_MT_POSITION_X) + if ((event.code) == (ABS_MT_SLOT)) { - if (platform.touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[platform.touchSlot].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; // Scale according to absRange + platform.touchSlot = event.value; + isMultitouch = true; } - if (event.code == ABS_MT_POSITION_Y) + if ((event.code) == (ABS_MT_POSITION_X)) { - if (platform.touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[platform.touchSlot].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; // Scale according to absRange - } - - if (event.code == ABS_MT_TRACKING_ID) - { - if ((event.value < 0) && (platform.touchSlot < MAX_TOUCH_POINTS)) + isMultitouch = true; + if ((platform.touchSlot) < (MAX_TOUCH_POINTS)) { - // Touch has ended for this point - CORE.Input.Touch.position[platform.touchSlot].x = -1; - CORE.Input.Touch.position[platform.touchSlot].y = -1; + platform.touchPosition[platform.touchSlot].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; + + // If this slot is active, it's a move. If not, we are just updating the buffer for when it becomes active. + // Only set to MOVE if we haven't already detected a DOWN or UP event this frame + if (platform.touchActive[platform.touchSlot] && touchAction == -1) touchAction = 2; // TOUCH_ACTION_MOVE + } + } + + if ((event.code) == (ABS_MT_POSITION_Y)) + { + if ((platform.touchSlot) < (MAX_TOUCH_POINTS)) + { + platform.touchPosition[platform.touchSlot].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; + + // If this slot is active, it's a move. If not, we are just updating the buffer for when it becomes active. + // Only set to MOVE if we haven't already detected a DOWN or UP event this frame + if (platform.touchActive[platform.touchSlot] && touchAction == -1) touchAction = 2; // TOUCH_ACTION_MOVE + } + } + + if ((event.code) == (ABS_MT_TRACKING_ID)) + { + if ((platform.touchSlot) < (MAX_TOUCH_POINTS)) + { + if (event.value >= 0) + { + + platform.touchActive[platform.touchSlot] = true; + platform.touchId[platform.touchSlot] = event.value; // Use Tracking ID for unique IDs + + touchAction = 1; // TOUCH_ACTION_DOWN + } + else + { + // Touch has ended for this point + platform.touchActive[platform.touchSlot] = false; + platform.touchPosition[platform.touchSlot].x = -1; + platform.touchPosition[platform.touchSlot].y = -1; + platform.touchId[platform.touchSlot] = -1; + + // Force UP action if we haven't already set a DOWN action + // (DOWN takes priority over UP if both happen in one frame, though rare) + if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP + } + } + } + + // Handle ABS_MT_PRESSURE (0x3a) if available, as some devices use it for lift-off + #ifndef ABS_MT_PRESSURE + #define ABS_MT_PRESSURE 0x3a + #endif + if ((event.code) == (ABS_MT_PRESSURE)) + { + if ((platform.touchSlot) < (MAX_TOUCH_POINTS)) + { + if (event.value <= 0) // Pressure 0 means lift + { + platform.touchActive[platform.touchSlot] = false; + platform.touchPosition[platform.touchSlot].x = -1; + platform.touchPosition[platform.touchSlot].y = -1; + platform.touchId[platform.touchSlot] = -1; + if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP + } } } // Touchscreen tap - if (event.code == ABS_PRESSURE) + if ((event.code) == (ABS_PRESSURE)) { int previousMouseLeftButtonState = platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT]; - if (!event.value && previousMouseLeftButtonState) + if ((!event.value) && (previousMouseLeftButtonState)) { platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0; - touchAction = 0; // TOUCH_ACTION_UP + + if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP } - if (event.value && !previousMouseLeftButtonState) + if ((event.value) && (!previousMouseLeftButtonState)) { platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1; touchAction = 1; // TOUCH_ACTION_DOWN @@ -2340,8 +2449,46 @@ static void PollMouseEvents(void) { platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value; - if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN - else touchAction = 0; // TOUCH_ACTION_UP + if (event.value > 0) + { + bool activateSlot0 = false; + + if (event.code == BTN_LEFT) + { + activateSlot0 = true; // Mouse click always activates + } + else if (event.code == BTN_TOUCH) + { + bool anyActive = false; + for (int i = 0; i < MAX_TOUCH_POINTS; i++) { + if (platform.touchActive[i]) { anyActive = true; break; } + } + if (!anyActive) activateSlot0 = true; + } + + if (activateSlot0) + { + platform.touchActive[0] = true; + platform.touchId[0] = 0; + } + + touchAction = 1; // TOUCH_ACTION_DOWN + } + else + { + // Only clear touch 0 for actual mouse clicks (BTN_LEFT) + if (event.code == BTN_LEFT) + { + platform.touchActive[0] = false; + platform.touchPosition[0].x = -1; + platform.touchPosition[0].y = -1; + } + else if (event.code == BTN_TOUCH) + { + platform.touchSlot = 0; // Reset slot index to 0 + } + touchAction = 0; // TOUCH_ACTION_UP + } } if (event.code == BTN_RIGHT) platform.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value; @@ -2362,11 +2509,33 @@ static void PollMouseEvents(void) if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y; } - // Update touch point count - CORE.Input.Touch.pointCount = 0; + // Repack active touches into CORE.Input.Touch + int k = 0; for (int i = 0; i < MAX_TOUCH_POINTS; i++) { - if (CORE.Input.Touch.position[i].x >= 0) CORE.Input.Touch.pointCount++; + if (platform.touchActive[i]) + { + CORE.Input.Touch.position[k] = platform.touchPosition[i]; + CORE.Input.Touch.pointId[k] = platform.touchId[i]; + k++; + } + } + CORE.Input.Touch.pointCount = k; + + // Clear remaining slots + for (int i = k; i < MAX_TOUCH_POINTS; i++) + { + CORE.Input.Touch.position[i].x = -1; + CORE.Input.Touch.position[i].y = -1; + CORE.Input.Touch.pointId[i] = -1; + } + + // Debug logging + static int lastTouchCount = 0; + if (CORE.Input.Touch.pointCount != lastTouchCount && (touchAction == 0 || touchAction == 1)) + { + TRACELOG(LOG_DEBUG, "TOUCH: Count changed from %d to %d (action: %d)", lastTouchCount, CORE.Input.Touch.pointCount, touchAction); + lastTouchCount = CORE.Input.Touch.pointCount; } #if defined(SUPPORT_GESTURES_SYSTEM) @@ -2558,4 +2727,4 @@ static void SetupFramebuffer(int width, int height) } } -// EOF +// EOF \ No newline at end of file From 2b48cf67936eace2a4aa58f7e22c34170883f0a8 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 29 Dec 2025 13:06:05 +0100 Subject: [PATCH 038/117] Formating review --- src/platforms/rcore_drm.c | 104 ++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 60 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index b97eac5f5..103d81975 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -2061,21 +2061,21 @@ static void ConfigureEvdevDevice(char *device) { bool prioritize = false; - // Priority logic: Touchscreens override Mice. - // 1. No device set yet? Take it. + // Priority logic: touchscreens override Mice + // 1. No device set yet? Take it if (platform.mouseFd == -1) prioritize = true; - // 2. Current is Mouse, New is Touch? Upgrade to Touch. + // 2. Current is mouse, new is touch? Upgrade to touch else if (isTouch && !platform.mouseIsTouch) prioritize = true; - // 3. Current is Touch, New is Touch? Use the new one (Last one found wins, standard behavior). + // 3. Current is touch, new is touch? Use the new one (last one found wins, standard behavior) else if (isTouch && platform.mouseIsTouch) prioritize = true; - // 4. Current is Mouse, New is Mouse? Use the new one. + // 4. Current is mouse, new is mouse? Use the new one else if (!isTouch && !platform.mouseIsTouch) prioritize = true; - // 5. Current is Touch, New is Mouse? IGNORE the mouse. Keep the touchscreen. + // 5. Current is touch, new is mouse? Ignore the mouse, keep the touchscreen else prioritize = false; if (prioritize) { - deviceKindStr = isTouch ? "touchscreen" : "mouse"; + deviceKindStr = isTouch? "touchscreen" : "mouse"; if (platform.mouseFd != -1) { @@ -2172,18 +2172,15 @@ static void PollKeyboardEvents(void) // If the event was a key, we know a working keyboard is connected, so disable the SSH keyboard platform.eventKeyboardMode = true; #endif - // Keyboard keys appear for codes 1 to 255, ignore everthing else if ((event.code >= 1) && (event.code <= 255)) { - // Lookup the scancode in the keymap to get a keycode keycode = linuxToRaylibMap[event.code]; // Make sure we got a valid keycode if ((keycode > 0) && (keycode < MAX_KEYBOARD_KEYS)) { - // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt // Event interface: 'value' is the value the event carries. Either a relative change for EV_REL, // absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for release, 1 for keypress and 2 for autorepeat @@ -2232,16 +2229,15 @@ static void PollGamepadEvents(void) { if (event.code < KEYMAP_SIZE) { - short keycodeRaylib = linuxToRaylibMap[event.code]; + short keycode = linuxToRaylibMap[event.code]; // raylib keycode - TRACELOG(LOG_DEBUG, "INPUT: Gamepad %2i: KEY_%s Keycode(linux): %4i Keycode(raylib): %4i", i, (event.value == 0)? "UP" : "DOWN", event.code, keycodeRaylib); + TRACELOG(LOG_DEBUG, "INPUT: Gamepad %2i: KEY_%s Keycode(linux): %4i Keycode(raylib): %4i", i, (event.value == 0)? "UP" : "DOWN", event.code, keycode); - if ((keycodeRaylib != 0) && (keycodeRaylib < MAX_GAMEPAD_BUTTONS)) + if ((keycode != 0) && (keycode < MAX_GAMEPAD_BUTTONS)) { // 1 - button pressed, 0 - button released - CORE.Input.Gamepad.currentButtonState[i][keycodeRaylib] = event.value; - - CORE.Input.Gamepad.lastButtonPressed = (event.value == 1)? keycodeRaylib : GAMEPAD_BUTTON_UNKNOWN; + CORE.Input.Gamepad.currentButtonState[i][keycode] = event.value; + CORE.Input.Gamepad.lastButtonPressed = (event.value == 1)? keycode : GAMEPAD_BUTTON_UNKNOWN; } } } @@ -2323,7 +2319,7 @@ static void PollMouseEvents(void) CORE.Input.Mouse.currentPosition.x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; // Scale according to absRange // Update single touch position only if it's active and no MT events are being used - if ((platform.touchActive[0]) && (!isMultitouch)) + if (platform.touchActive[0] && !isMultitouch) { platform.touchPosition[0].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; if (touchAction == -1) touchAction = 2; // TOUCH_ACTION_MOVE @@ -2335,7 +2331,7 @@ static void PollMouseEvents(void) CORE.Input.Mouse.currentPosition.y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; // Scale according to absRange // Update single touch position only if it's active and no MT events are being used - if ((platform.touchActive[0]) && (!isMultitouch)) + if (platform.touchActive[0] && !isMultitouch) { platform.touchPosition[0].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; if (touchAction == -1) touchAction = 2; // TOUCH_ACTION_MOVE @@ -2343,16 +2339,16 @@ static void PollMouseEvents(void) } // Multitouch movement - if ((event.code) == (ABS_MT_SLOT)) + if (event.code == ABS_MT_SLOT) { platform.touchSlot = event.value; isMultitouch = true; } - if ((event.code) == (ABS_MT_POSITION_X)) + if (event.code == ABS_MT_POSITION_X) { isMultitouch = true; - if ((platform.touchSlot) < (MAX_TOUCH_POINTS)) + if (platform.touchSlot < MAX_TOUCH_POINTS) { platform.touchPosition[platform.touchSlot].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; @@ -2362,9 +2358,9 @@ static void PollMouseEvents(void) } } - if ((event.code) == (ABS_MT_POSITION_Y)) + if (event.code == ABS_MT_POSITION_Y) { - if ((platform.touchSlot) < (MAX_TOUCH_POINTS)) + if (platform.touchSlot < MAX_TOUCH_POINTS) { platform.touchPosition[platform.touchSlot].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; @@ -2374,9 +2370,9 @@ static void PollMouseEvents(void) } } - if ((event.code) == (ABS_MT_TRACKING_ID)) + if (event.code == ABS_MT_TRACKING_ID) { - if ((platform.touchSlot) < (MAX_TOUCH_POINTS)) + if (platform.touchSlot < MAX_TOUCH_POINTS) { if (event.value >= 0) { @@ -2384,7 +2380,7 @@ static void PollMouseEvents(void) platform.touchActive[platform.touchSlot] = true; platform.touchId[platform.touchSlot] = event.value; // Use Tracking ID for unique IDs - touchAction = 1; // TOUCH_ACTION_DOWN + touchAction = 1; // TOUCH_ACTION_DOWN } else { @@ -2396,18 +2392,18 @@ static void PollMouseEvents(void) // Force UP action if we haven't already set a DOWN action // (DOWN takes priority over UP if both happen in one frame, though rare) - if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP + if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP } } } // Handle ABS_MT_PRESSURE (0x3a) if available, as some devices use it for lift-off #ifndef ABS_MT_PRESSURE - #define ABS_MT_PRESSURE 0x3a + #define ABS_MT_PRESSURE 0x3a #endif - if ((event.code) == (ABS_MT_PRESSURE)) + if (event.code == ABS_MT_PRESSURE) { - if ((platform.touchSlot) < (MAX_TOUCH_POINTS)) + if (platform.touchSlot < MAX_TOUCH_POINTS) { if (event.value <= 0) // Pressure 0 means lift { @@ -2415,30 +2411,28 @@ static void PollMouseEvents(void) platform.touchPosition[platform.touchSlot].x = -1; platform.touchPosition[platform.touchSlot].y = -1; platform.touchId[platform.touchSlot] = -1; - if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP + if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP } } } // Touchscreen tap - if ((event.code) == (ABS_PRESSURE)) + if (event.code == ABS_PRESSURE) { int previousMouseLeftButtonState = platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT]; - if ((!event.value) && (previousMouseLeftButtonState)) + if (!event.value && previousMouseLeftButtonState) { platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0; - - if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP + if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP } - if ((event.value) && (!previousMouseLeftButtonState)) + if (event.value && !previousMouseLeftButtonState) { platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1; - touchAction = 1; // TOUCH_ACTION_DOWN + touchAction = 1; // TOUCH_ACTION_DOWN } } - } // Button parsing @@ -2453,16 +2447,15 @@ static void PollMouseEvents(void) { bool activateSlot0 = false; - if (event.code == BTN_LEFT) - { - activateSlot0 = true; // Mouse click always activates - } + if (event.code == BTN_LEFT) activateSlot0 = true; // Mouse click always activates else if (event.code == BTN_TOUCH) { bool anyActive = false; - for (int i = 0; i < MAX_TOUCH_POINTS; i++) { + for (int i = 0; i < MAX_TOUCH_POINTS; i++) + { if (platform.touchActive[i]) { anyActive = true; break; } } + if (!anyActive) activateSlot0 = true; } @@ -2472,7 +2465,7 @@ static void PollMouseEvents(void) platform.touchId[0] = 0; } - touchAction = 1; // TOUCH_ACTION_DOWN + touchAction = 1; // TOUCH_ACTION_DOWN } else { @@ -2483,10 +2476,8 @@ static void PollMouseEvents(void) platform.touchPosition[0].x = -1; platform.touchPosition[0].y = -1; } - else if (event.code == BTN_TOUCH) - { - platform.touchSlot = 0; // Reset slot index to 0 - } + else if (event.code == BTN_TOUCH) platform.touchSlot = 0; // Reset slot index to 0 + touchAction = 0; // TOUCH_ACTION_UP } } @@ -2503,10 +2494,12 @@ static void PollMouseEvents(void) if (!CORE.Input.Mouse.cursorLocked) { if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0; - if (CORE.Input.Mouse.currentPosition.x > CORE.Window.screen.width/CORE.Input.Mouse.scale.x) CORE.Input.Mouse.currentPosition.x = CORE.Window.screen.width/CORE.Input.Mouse.scale.x; + if (CORE.Input.Mouse.currentPosition.x > CORE.Window.screen.width/CORE.Input.Mouse.scale.x) + CORE.Input.Mouse.currentPosition.x = CORE.Window.screen.width/CORE.Input.Mouse.scale.x; if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0; - if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y; + if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) + CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y; } // Repack active touches into CORE.Input.Touch @@ -2520,6 +2513,7 @@ static void PollMouseEvents(void) k++; } } + CORE.Input.Touch.pointCount = k; // Clear remaining slots @@ -2529,20 +2523,11 @@ static void PollMouseEvents(void) CORE.Input.Touch.position[i].y = -1; CORE.Input.Touch.pointId[i] = -1; } - - // Debug logging - static int lastTouchCount = 0; - if (CORE.Input.Touch.pointCount != lastTouchCount && (touchAction == 0 || touchAction == 1)) - { - TRACELOG(LOG_DEBUG, "TOUCH: Count changed from %d to %d (action: %d)", lastTouchCount, CORE.Input.Touch.pointCount, touchAction); - lastTouchCount = CORE.Input.Touch.pointCount; - } #if defined(SUPPORT_GESTURES_SYSTEM) if (touchAction > -1) { GestureEvent gestureEvent = { 0 }; - gestureEvent.touchAction = touchAction; gestureEvent.pointCount = CORE.Input.Touch.pointCount; @@ -2553,7 +2538,6 @@ static void PollMouseEvents(void) } ProcessGestureEvent(gestureEvent); - touchAction = -1; } #endif From 752373867741e044244df0e64695463598a4a742 Mon Sep 17 00:00:00 2001 From: Hamza RAHAL <77698738+hmz-rhl@users.noreply.github.com> Date: Tue, 30 Dec 2025 20:11:37 +0100 Subject: [PATCH 039/117] Add hilbert curve example (#5454) --- examples/shapes/shapes_hilbert_curve.c | 187 +++++++++++++++++++++++ examples/shapes/shapes_hilbert_curve.png | Bin 0 -> 15288 bytes 2 files changed, 187 insertions(+) create mode 100644 examples/shapes/shapes_hilbert_curve.c create mode 100644 examples/shapes/shapes_hilbert_curve.png diff --git a/examples/shapes/shapes_hilbert_curve.c b/examples/shapes/shapes_hilbert_curve.c new file mode 100644 index 000000000..1af263b34 --- /dev/null +++ b/examples/shapes/shapes_hilbert_curve.c @@ -0,0 +1,187 @@ +/******************************************************************************************* +* +* raylib [shapes] example - hilbert curve example +* +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 5.6, last time updated with raylib 5.6 +* +* Example contributed by Hamza RAHAL (@hmz-rhl) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025 Hamza RAHAL (@hmz-rhl) +* +********************************************************************************************/ + + +#include "raylib.h" +#include "raymath.h" +#include +#include + +const int screenWidth = 800; + +const int screenHeight = 450; + +int order = 2; + +int total; + +int counter = 0; + +Vector2 *hilbertPath = 0; + +const Vector2 hilbertPoints[4] = +{ + [0] = { + .x = 0, + .y = 0 + }, + [1] = { + .x = 0, + .y = 1 + }, + [2] = { + .x = 1, + .y = 1 + }, + [3] = { + .x = 1, + .y = 0 + }, +}; + +//------------------------------------------------------------------------------------ +// Module Functions Declaration +//------------------------------------------------------------------------------------ +Vector2 Hilbert(int index); + +void InitHilbertPath(void); + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - hilbert curve example"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + + InitHilbertPath(); + + //-------------------------------------------------------------------------------------- + + // Main game loop + //-------------------------------------------------------------------------------------- + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if ((IsKeyPressed(KEY_UP)) && (order < 8)) + { + counter = 0; + ++order; + InitHilbertPath(); + } + else if((IsKeyPressed(KEY_DOWN)) && (order > 1)) + { + counter = 0; + --order; + InitHilbertPath(); + } + //---------------------------------------------------------------------------------- + + // Draw + //-------------------------------------------------------------------------- + BeginDrawing(); + DrawText(TextFormat("(press UP or DOWN to change)\norder : %d", order), screenWidth/2 + 70, 25, 20, WHITE); + + if(counter < total) + { + ClearBackground(BLACK); + for (int i = 1; i <= counter; i++) + { + DrawLineV(hilbertPath[i], hilbertPath[i-1], ColorFromHSV(((float)i / total) * 360.0f, 1.0f, 1.0f)); + } + counter += 1; + } + EndDrawing(); + //-------------------------------------------------------------------------- + } + //-------------------------------------------------------------------------------------- + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + MemFree(hilbertPath); + //-------------------------------------------------------------------------------------- + return 0; +} + +//------------------------------------------------------------------------------------ +// Module Functions Definition +//------------------------------------------------------------------------------------ + +// calculate U positions +Vector2 Hilbert(int index) +{ + + int hiblertIndex = index&3; + Vector2 vect = hilbertPoints[hiblertIndex]; + float temp; + int len; + + for (int j = 1; j < order; j++) + { + index = index>>2; + hiblertIndex = index&3; + len = 1<L25DS|?4aZm(`)LLPMDx!!?6_5^(B9X^{>D>eiiCHMqA9n2hvy(|6d+v9> z^L^*uJxTQTTu0Y4(nAo0?(XL5gCJUF2!d8pb-+6d0=}z75QkWI*EPNo9|Qyn1VeRf zJ-&;8qpkT8r_ZY37~Q~FY$J;13->&O*&Qd%Da9E~x=6zXmEiid!o`|DB>o|s#kSY7 z%E((s7DnfM3TB*wSKE@sz-~qjJ+w5(2Li8Jq0QgJ`o*+yokn>)$ z#vFh})-PA!oESQ?<-!OxOuQ|%1#gKl zYm@4D%i^~9Lj7p-w3JYnB~G8p7L>)-(&zXzyi*uqHt)*k&DPs~$xS`m`m;<oWjORZHLiB+ky1)41HvpmEb-3Ttwi$t6pJR%;r7L>z(W^JxRROGiUGx~lJN=0{s$POl@ zoQyo)z)kn+%`y*nP4_*F+mA}N=eU@*<=)8;sLpT+J~~$~*6)cOul;0(Tz9?Wxu=H# z^Gcf45;q}9M0o<{SbVP%kA%hTx``BrH2JStTM+3tYvu2q^k<(elid!s2)q=y$?w;u zO-4=w;dcq(OQ!^$C|%XMp}EA}E`3&H#tPG$8(B}kGxs6x>eNjNW|@Gq)NEy;*Miv$u^?JQJlQ;t>On+i}qPtYIU1@G}x5#$Nn87Z_{k{39|YCCCU`pj+xSXRgj{iT@s|Q+<^Tj z-jK=P{D4q7q#P`9Iv2I0O2qU+?nlEkD{4 zW?@Gv0)$yaO{KJi*b_SRFGN6u8>ZmCj#HJIdfP#*HX#xnN49fn%u3wU9x%^xW!EMyi?2*D^TWYx&?KJ)X~)P1*GT zjsu`o-yD&DD>`iIoFPpJqnfkXZ2J>6ZQlAKISorcke4gqOX9(c$qtY=pCaX+dXX&Y z06ut!F?2N+cy;6Ej#$YUO-jScYjK~T$6_SgaZYFdH$AClcaQQ`BANNjPRE5<6raE0 zGl0np7P|}x{}*aHJK?v4_YA(G04R&?ZI_UIODGLeNFh3)v|$5M4fxS~(lZk;igu z=ua3CY@_I^#o9gA(xB?>MXASWQtaJ>YWvcvD_R#2rf(8sBO$i-w{<3PjN$2GRDYi7YR#G7!=H^;T`H#S3 z(^=f>mM4NLvyI~WX>vmoD``!ukj44?9^7(t0uJOLeGRjg_rGQ6E1_Ck(te07wn zkHLKZm`w(A@{Uwx;Az?^EMmgsH6QxH5Qxk}6d(#Oy%%x;ashH-;uIFDBhiCG6d(#O zJ8{Sb$OXs+LK7hhub?0>?C~E@Q$r9;5&O literal 0 HcmV?d00001 From 6dfaf9fe7edfee08eac7bfabcc3dabcc5147530d Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 30 Dec 2025 21:19:54 +0100 Subject: [PATCH 040/117] REVIEWED: example `shapes_hilbert_curve` #5454 Make it more didactic and dynamic, avoid global variables --- examples/shapes/shapes_hilbert_curve.c | 233 ++++++++++++----------- examples/shapes/shapes_hilbert_curve.png | Bin 15288 -> 16975 bytes 2 files changed, 121 insertions(+), 112 deletions(-) diff --git a/examples/shapes/shapes_hilbert_curve.c b/examples/shapes/shapes_hilbert_curve.c index 1af263b34..8ea03c7c5 100644 --- a/examples/shapes/shapes_hilbert_curve.c +++ b/examples/shapes/shapes_hilbert_curve.c @@ -6,7 +6,7 @@ * * Example originally created with raylib 5.6, last time updated with raylib 5.6 * -* Example contributed by Hamza RAHAL (@hmz-rhl) +* Example contributed by Hamza RAHAL (@hmz-rhl) and reviewed by Ramon Santamaria (@raysan5) * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software @@ -17,48 +17,18 @@ #include "raylib.h" -#include "raymath.h" -#include -#include -const int screenWidth = 800; +#define RAYGUI_IMPLEMENTATION +#include "raygui.h" -const int screenHeight = 450; - -int order = 2; - -int total; - -int counter = 0; - -Vector2 *hilbertPath = 0; - -const Vector2 hilbertPoints[4] = -{ - [0] = { - .x = 0, - .y = 0 - }, - [1] = { - .x = 0, - .y = 1 - }, - [2] = { - .x = 1, - .y = 1 - }, - [3] = { - .x = 1, - .y = 0 - }, -}; +#include // Required for: calloc(), free() //------------------------------------------------------------------------------------ // Module Functions Declaration //------------------------------------------------------------------------------------ -Vector2 Hilbert(int index); - -void InitHilbertPath(void); +static Vector2 *LoadHilbertPath(int order, float size, int *strokeCount); +static void UnloadHilbertPath(Vector2 *hilbertPath); +static Vector2 ComputeHilbertStep(int order, int index); //------------------------------------------------------------------------------------ // Program main entry point @@ -67,13 +37,23 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - hilbert curve example"); + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - hilbert curve"); - SetTargetFPS(60); // Set our game to run at 60 frames-per-second - - InitHilbertPath(); + int order = 2; + float size = GetScreenHeight(); + int strokeCount = 0; + Vector2 *hilbertPath = LoadHilbertPath(order, size, &strokeCount); + int prevOrder = order; + int prevSize = (int)size; // NOTE: Size from slider is float but for comparison we use int + int counter = 0; + float thick = 2.0f; + bool animate = true; + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop @@ -82,34 +62,52 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - if ((IsKeyPressed(KEY_UP)) && (order < 8)) + // Check if order or size have changed to regenerate + // NOTE: Size from slider is float but for comparison we use int + if ((prevOrder != order) || (prevSize != (int)size)) { - counter = 0; - ++order; - InitHilbertPath(); - } - else if((IsKeyPressed(KEY_DOWN)) && (order > 1)) - { - counter = 0; - --order; - InitHilbertPath(); + UnloadHilbertPath(hilbertPath); + hilbertPath = LoadHilbertPath(order, size, &strokeCount); + + if (animate) counter = 0; + else counter = strokeCount; + + prevOrder = order; + prevSize = size; } //---------------------------------------------------------------------------------- // Draw //-------------------------------------------------------------------------- BeginDrawing(); - DrawText(TextFormat("(press UP or DOWN to change)\norder : %d", order), screenWidth/2 + 70, 25, 20, WHITE); - if(counter < total) - { - ClearBackground(BLACK); - for (int i = 1; i <= counter; i++) + ClearBackground(RAYWHITE); + + if (counter < strokeCount) { - DrawLineV(hilbertPath[i], hilbertPath[i-1], ColorFromHSV(((float)i / total) * 360.0f, 1.0f, 1.0f)); + // Draw Hilbert path animation, one stroke every frame + for (int i = 1; i <= counter; i++) + { + DrawLineEx(hilbertPath[i], hilbertPath[i - 1], thick, ColorFromHSV(((float)i/strokeCount)*360.0f, 1.0f, 1.0f)); + } + + counter += 1; } - counter += 1; - } + else + { + // Draw full Hilbert path + for (int i = 1; i < strokeCount; i++) + { + DrawLineEx(hilbertPath[i], hilbertPath[i - 1], thick, ColorFromHSV(((float)i/strokeCount)*360.0f, 1.0f, 1.0f)); + } + } + + // Draw UI using raygui + GuiCheckBox((Rectangle){ 450, 50, 20, 20 }, "ANIMATE GENERATION ON CHANGE", &animate); + GuiSpinner((Rectangle){ 585, 100, 180, 30 }, "HILBERT CURVE ORDER: ", &order, 2, 8, false); + GuiSlider((Rectangle){ 524, 150, 240, 24 }, "THICKNESS: ", NULL, &thick, 1.0f, 10.0f); + GuiSlider((Rectangle){ 524, 190, 240, 24 }, "TOTAL SIZE: ", NULL, &size, 10.0f, GetScreenHeight()*1.5f); + EndDrawing(); //-------------------------------------------------------------------------- } @@ -117,8 +115,9 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- + UnloadHilbertPath(hilbertPath); + CloseWindow(); // Close window and OpenGL context - MemFree(hilbertPath); //-------------------------------------------------------------------------------------- return 0; } @@ -126,62 +125,72 @@ int main(void) //------------------------------------------------------------------------------------ // Module Functions Definition //------------------------------------------------------------------------------------ - -// calculate U positions -Vector2 Hilbert(int index) +// Load the whole Hilbert Path (including each U and their link) +static Vector2 *LoadHilbertPath(int order, float size, int *strokeCount) { + int N = 1 << order; + float len = size/N; + *strokeCount = N*N; - int hiblertIndex = index&3; - Vector2 vect = hilbertPoints[hiblertIndex]; - float temp; - int len; - - for (int j = 1; j < order; j++) + Vector2 *hilbertPath = (Vector2 *)RL_CALLOC(*strokeCount, sizeof(Vector2)); + + for (int i = 0; i < *strokeCount; i++) { - index = index>>2; - hiblertIndex = index&3; - len = 1<> 2; + hilbertIndex = index&3; + len = 1 << j; + + switch (hilbertIndex) + { + case 0: + { + temp = vect.x; + vect.x = vect.y; + vect.y = temp; + } break; + case 2: vect.x += len; + case 1: vect.y += len; break; + case 3: + { + temp = len - 1 - vect.x; + vect.x = 2*len - 1 - vect.y; + vect.y = temp; + } break; + default: break; + } + } + + return vect; } diff --git a/examples/shapes/shapes_hilbert_curve.png b/examples/shapes/shapes_hilbert_curve.png index cbb3d0753b154bac0cc160d140ab5c27e59dbddb..af99cbc493115d0de96f50ce8391f5f6aae85028 100644 GIT binary patch literal 16975 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 FKLA35BUk_c literal 15288 zcmeHOYfuwc6y6O88B8z^js}!REJC&87$JZKffz7?%0r~;pshg=BZ$=&5kVfYAru<~ zw8a6cg>L25DS|?4aZm(`)LLPMDx!!?6_5^(B9X^{>D>eiiCHMqA9n2hvy(|6d+v9> z^L^*uJxTQTTu0Y4(nAo0?(XL5gCJUF2!d8pb-+6d0=}z75QkWI*EPNo9|Qyn1VeRf zJ-&;8qpkT8r_ZY37~Q~FY$J;13->&O*&Qd%Da9E~x=6zXmEiid!o`|DB>o|s#kSY7 z%E((s7DnfM3TB*wSKE@sz-~qjJ+w5(2Li8Jq0QgJ`o*+yokn>)$ z#vFh})-PA!oESQ?<-!OxOuQ|%1#gKl zYm@4D%i^~9Lj7p-w3JYnB~G8p7L>)-(&zXzyi*uqHt)*k&DPs~$xS`m`m;<oWjORZHLiB+ky1)41HvpmEb-3Ttwi$t6pJR%;r7L>z(W^JxRROGiUGx~lJN=0{s$POl@ zoQyo)z)kn+%`y*nP4_*F+mA}N=eU@*<=)8;sLpT+J~~$~*6)cOul;0(Tz9?Wxu=H# z^Gcf45;q}9M0o<{SbVP%kA%hTx``BrH2JStTM+3tYvu2q^k<(elid!s2)q=y$?w;u zO-4=w;dcq(OQ!^$C|%XMp}EA}E`3&H#tPG$8(B}kGxs6x>eNjNW|@Gq)NEy;*Miv$u^?JQJlQ;t>On+i}qPtYIU1@G}x5#$Nn87Z_{k{39|YCCCU`pj+xSXRgj{iT@s|Q+<^Tj z-jK=P{D4q7q#P`9Iv2I0O2qU+?nlEkD{4 zW?@Gv0)$yaO{KJi*b_SRFGN6u8>ZmCj#HJIdfP#*HX#xnN49fn%u3wU9x%^xW!EMyi?2*D^TWYx&?KJ)X~)P1*GT zjsu`o-yD&DD>`iIoFPpJqnfkXZ2J>6ZQlAKISorcke4gqOX9(c$qtY=pCaX+dXX&Y z06ut!F?2N+cy;6Ej#$YUO-jScYjK~T$6_SgaZYFdH$AClcaQQ`BANNjPRE5<6raE0 zGl0np7P|}x{}*aHJK?v4_YA(G04R&?ZI_UIODGLeNFh3)v|$5M4fxS~(lZk;igu z=ua3CY@_I^#o9gA(xB?>MXASWQtaJ>YWvcvD_R#2rf(8sBO$i-w{<3PjN$2GRDYi7YR#G7!=H^;T`H#S3 z(^=f>mM4NLvyI~WX>vmoD``!ukj44?9^7(t0uJOLeGRjg_rGQ6E1_Ck(te07wn zkHLKZm`w(A@{Uwx;Az?^EMmgsH6QxH5Qxk}6d(#Oy%%x;ashH-;uIFDBhiCG6d(#O zJ8{Sb$OXs+LK7hhub?0>?C~E@Q$r9;5&O From ebf2f61425899735c48f70dc5737c2b03a137e80 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 30 Dec 2025 22:03:36 +0100 Subject: [PATCH 041/117] Delete core_input_keyboard_gamepad_test.c --- .../core/core_input_keyboard_gamepad_test.c | 173 ------------------ 1 file changed, 173 deletions(-) delete mode 100644 examples/core/core_input_keyboard_gamepad_test.c diff --git a/examples/core/core_input_keyboard_gamepad_test.c b/examples/core/core_input_keyboard_gamepad_test.c deleted file mode 100644 index d1f9106f7..000000000 --- a/examples/core/core_input_keyboard_gamepad_test.c +++ /dev/null @@ -1,173 +0,0 @@ -/******************************************************************************************* -* -* raylib [core] example - Keyboard vs Gamepad Input Test -* -* Example complexity rating: [★☆☆☆] 1/4 -* -* This example is a diagnostic tool to verify that keyboard input is not -* incorrectly detected as gamepad input on Android devices. -* -* Issue reference: https://github.com/raysan5/raylib/issues/5387 -* -* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, -* BSD-like license that allows static linking with closed source software -* -* Copyright (c) 2025 raylib contributors -* -********************************************************************************************/ - -#include "raylib.h" - -//------------------------------------------------------------------------------------ -// Program main entry point -//------------------------------------------------------------------------------------ -int main(void) -{ - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [core] example - keyboard vs gamepad test"); - - Vector2 ballPosition = { (float)screenWidth/2, (float)screenHeight/2 }; - int lastKeyPressed = 0; - - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) - { - // Update - //---------------------------------------------------------------------------------- - - // Track keyboard input - if (IsKeyDown(KEY_RIGHT)) ballPosition.x += 4.0f; - if (IsKeyDown(KEY_LEFT)) ballPosition.x -= 4.0f; - if (IsKeyDown(KEY_UP)) ballPosition.y -= 4.0f; - if (IsKeyDown(KEY_DOWN)) ballPosition.y += 4.0f; - - // Keep ball on screen - if (ballPosition.x < 25) ballPosition.x = 25; - if (ballPosition.x > screenWidth - 25) ballPosition.x = screenWidth - 25; - if (ballPosition.y < 25) ballPosition.y = 25; - if (ballPosition.y > screenHeight - 25) ballPosition.y = screenHeight - 25; - - // Track last key pressed - int key = GetKeyPressed(); - if (key != 0) lastKeyPressed = key; - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(RAYWHITE); - - // Title - DrawText("KEYBOARD vs GAMEPAD INPUT TEST", 180, 10, 20, DARKGRAY); - DrawText("Issue #5387: Keyboard detected as gamepad on some Android devices", 120, 35, 14, GRAY); - - // Divider - DrawLine(0, 60, screenWidth, 60, LIGHTGRAY); - - // Keyboard section - DrawText("KEYBOARD INPUT", 20, 75, 18, DARKBLUE); - DrawRectangle(20, 100, 360, 80, Fade(BLUE, 0.1f)); - - DrawText(TextFormat("Arrow Keys: [%s] [%s] [%s] [%s]", - IsKeyDown(KEY_UP) ? "UP" : "--", - IsKeyDown(KEY_DOWN) ? "DN" : "--", - IsKeyDown(KEY_LEFT) ? "LT" : "--", - IsKeyDown(KEY_RIGHT) ? "RT" : "--"), 30, 110, 16, BLACK); - - DrawText(TextFormat("Last Key Pressed: %d", lastKeyPressed), 30, 135, 16, DARKGRAY); - DrawText(TextFormat("Any Key Down: %s", (IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || - IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT)) ? "YES" : "NO"), 30, 155, 16, DARKGRAY); - - // Gamepad section - DrawText("GAMEPAD STATUS", 420, 75, 18, DARKGREEN); - DrawRectangle(420, 100, 360, 80, Fade(GREEN, 0.1f)); - - bool gamepadReady = IsGamepadAvailable(0); - DrawText(TextFormat("Gamepad 0 Available: %s", gamepadReady ? "YES" : "NO"), - 430, 110, 16, gamepadReady ? RED : DARKGREEN); - - if (gamepadReady) - { - DrawText(TextFormat("D-Pad: [%s] [%s] [%s] [%s]", - IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_UP) ? "UP" : "--", - IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_DOWN) ? "DN" : "--", - IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_LEFT) ? "LT" : "--", - IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_RIGHT) ? "RT" : "--"), - 430, 135, 16, RED); - - DrawText(TextFormat("Gamepad Name: %.20s", GetGamepadName(0)), 430, 155, 14, DARKGRAY); - } - else - { - DrawText("No gamepad detected", 430, 135, 16, DARKGREEN); - } - - // Divider - DrawLine(0, 190, screenWidth, 190, LIGHTGRAY); - - // Test result section - DrawText("TEST RESULT", 20, 200, 18, MAROON); - - bool keyboardActive = IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || - IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT); - - if (keyboardActive && gamepadReady) - { - // BUG DETECTED: Keyboard is triggering gamepad detection - DrawRectangle(20, 225, 760, 50, Fade(RED, 0.3f)); - DrawText("BUG DETECTED: Keyboard input is being detected as gamepad!", 30, 235, 18, RED); - DrawText("The fix for issue #5387 may not be working correctly.", 30, 258, 14, DARKGRAY); - } - else if (keyboardActive && !gamepadReady) - { - // CORRECT: Keyboard works without triggering gamepad - DrawRectangle(20, 225, 760, 50, Fade(GREEN, 0.3f)); - DrawText("PASS: Keyboard input detected correctly (no phantom gamepad)", 30, 235, 18, DARKGREEN); - DrawText("Issue #5387 fix is working as expected.", 30, 258, 14, DARKGRAY); - } - else if (!keyboardActive && gamepadReady) - { - // Gamepad is connected (might be real or might be bug on idle) - DrawRectangle(20, 225, 760, 50, Fade(ORANGE, 0.3f)); - DrawText("INFO: Gamepad detected - press keyboard keys to test", 30, 235, 18, ORANGE); - DrawText("If gamepad stays active while pressing keyboard = BUG", 30, 258, 14, DARKGRAY); - } - else - { - // Idle state - DrawRectangle(20, 225, 760, 50, Fade(GRAY, 0.1f)); - DrawText("WAITING: Press arrow keys to test keyboard input", 30, 235, 18, GRAY); - DrawText("Gamepad should NOT become available when pressing keyboard keys", 30, 258, 14, DARKGRAY); - } - - // Ball controlled by keyboard - DrawText("Ball Control (Arrow Keys):", 20, 295, 16, DARKGRAY); - DrawCircleV(ballPosition, 25, MAROON); - DrawCircleLines((int)ballPosition.x, (int)ballPosition.y, 25, DARKGRAY); - - // Instructions - DrawRectangle(0, screenHeight - 45, screenWidth, 45, Fade(BLACK, 0.05f)); - DrawText("Instructions: Press keyboard arrow keys - the ball should move and gamepad should stay 'NO'", - 20, screenHeight - 35, 14, DARKGRAY); - DrawText("If gamepad becomes 'YES' while pressing keyboard = issue #5387 is NOT fixed", - 20, screenHeight - 18, 14, DARKGRAY); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - CloseWindow(); - //-------------------------------------------------------------------------------------- - - return 0; -} From 6e70dece560e344089b11597efe97847ce435310 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 30 Dec 2025 22:05:37 +0100 Subject: [PATCH 042/117] Update minshell.html --- src/minshell.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/minshell.html b/src/minshell.html index ec7158841..6e2f137eb 100644 --- a/src/minshell.html +++ b/src/minshell.html @@ -54,6 +54,12 @@ // 'Ask where to save each file before downloading' - which you can set true/false. // If you enable this setting it would always ask you and bring the SaveAsDialog saveAs(blob, localFSname); + + // Alternative implementation to avoid FileSaver.js + //const link = document.createElement("a"); + //link.href = URL.createObjectURL(blob); + //link.download = localFSname; + //link.click(); } From fa1d4eb7fa01a37df79e7a0643cbb0f590e4dd84 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 30 Dec 2025 22:06:05 +0100 Subject: [PATCH 043/117] Update shapes_hilbert_curve.c --- examples/shapes/shapes_hilbert_curve.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shapes/shapes_hilbert_curve.c b/examples/shapes/shapes_hilbert_curve.c index 8ea03c7c5..3f368ca03 100644 --- a/examples/shapes/shapes_hilbert_curve.c +++ b/examples/shapes/shapes_hilbert_curve.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [shapes] example - hilbert curve example +* raylib [shapes] example - hilbert curve * * Example complexity rating: [★★★☆] 3/4 * From f260f5fdd019a1eacab7f2d5edd91a9c2eb6a7a2 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 30 Dec 2025 22:06:18 +0100 Subject: [PATCH 044/117] Update Makefile --- examples/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/Makefile b/examples/Makefile index bc2afbb3c..9983d8705 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -575,6 +575,7 @@ SHAPES = \ shapes/shapes_easings_box \ shapes/shapes_easings_rectangles \ shapes/shapes_following_eyes \ + shapes/shapes_hilbert_curve \ shapes/shapes_kaleidoscope \ shapes/shapes_lines_bezier \ shapes/shapes_lines_drawing \ From 695f3535333594b4cb244e4ac1537e2affbe13d1 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 30 Dec 2025 22:07:23 +0100 Subject: [PATCH 045/117] Update Makefile.Web --- examples/Makefile.Web | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/Makefile.Web b/examples/Makefile.Web index f36113f15..2e74bd211 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -563,6 +563,7 @@ SHAPES = \ shapes/shapes_easings_box \ shapes/shapes_easings_rectangles \ shapes/shapes_following_eyes \ + shapes/shapes_hilbert_curve \ shapes/shapes_kaleidoscope \ shapes/shapes_lines_bezier \ shapes/shapes_lines_drawing \ @@ -914,6 +915,9 @@ shapes/shapes_easings_rectangles: shapes/shapes_easings_rectangles.c shapes/shapes_following_eyes: shapes/shapes_following_eyes.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +shapes/shapes_hilbert_curve: shapes/shapes_hilbert_curve.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + shapes/shapes_kaleidoscope: shapes/shapes_kaleidoscope.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) From 4054fc42f382ee4f161e953fbee30ddbee03d6cf Mon Sep 17 00:00:00 2001 From: RANDRIA Luca Date: Wed, 31 Dec 2025 00:09:20 +0300 Subject: [PATCH 046/117] Remove stdio.h unused header (#5456) --- examples/text/text_words_alignment.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/text/text_words_alignment.c b/examples/text/text_words_alignment.c index a558d5b11..dbd9cd03e 100644 --- a/examples/text/text_words_alignment.c +++ b/examples/text/text_words_alignment.c @@ -19,8 +19,6 @@ #include "raymath.h" // Required for: Lerp() -#include - typedef enum TextAlignment { TEXT_ALIGN_LEFT = 0, TEXT_ALIGN_TOP = 0, From 0c3e10b262127a162841df2d61b64143c525c2f7 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 30 Dec 2025 22:49:43 +0100 Subject: [PATCH 047/117] REVIEWED: `FileExists()`, using macro --- src/rcore.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index ea38300f1..af761cbb2 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -205,11 +205,13 @@ #define GETCWD _getcwd // NOTE: MSDN recommends not to use getcwd(), chdir() #define CHDIR _chdir #define MKDIR(dir) _mkdir(dir) + #define ACCESS(fn) _access(fn, 0) #else #include // Required for: getch(), chdir(), mkdir(), access() #define GETCWD getcwd #define CHDIR chdir #define MKDIR(dir) mkdir(dir, 0777) + #define ACCESS(fn) access(fn, F_OK) #endif //---------------------------------------------------------------------------------- @@ -1972,11 +1974,7 @@ bool FileExists(const char *fileName) { bool result = false; -#if defined(_WIN32) - if (_access(fileName, 0) != -1) result = true; -#else - if (access(fileName, F_OK) != -1) result = true; -#endif + if (ACCESS(fileName) != -1) result = true; // NOTE: Alternatively, stat() can be used instead of access() //#include From 9b183e0c5e5786a8a92e34e6a8f941586c12c39d Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 30 Dec 2025 23:21:10 +0100 Subject: [PATCH 048/117] REXM: Update examples and reports --- examples/Makefile.Web | 12 +- examples/README.md | 7 +- .../examples/shapes_hilbert_curve.vcxproj | 569 ++++++++++++++++++ projects/VS2022/raylib.sln | 29 +- tools/rexm/reports/examples_issues.md | 13 +- tools/rexm/reports/examples_validation.md | 17 +- 6 files changed, 622 insertions(+), 25 deletions(-) create mode 100644 projects/VS2022/examples/shapes_hilbert_curve.vcxproj diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 2e74bd211..5718088ac 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -1371,15 +1371,15 @@ shaders/shaders_fog_rendering: shaders/shaders_fog_rendering.c shaders/shaders_game_of_life: shaders/shaders_game_of_life.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/game_of_life.fs@resources/shaders/glsl100/game_of_life.fs \ - --preload-file shaders/resources/game_of_life/acorn.png@resources/game_of_life/acorn.png \ - --preload-file shaders/resources/game_of_life/breeder.png@resources/game_of_life/breeder.png \ + --preload-file shaders/resources/game_of_life/r_pentomino.png@resources/game_of_life/r_pentomino.png \ --preload-file shaders/resources/game_of_life/glider.png@resources/game_of_life/glider.png \ - --preload-file shaders/resources/game_of_life/glider_gun.png@resources/game_of_life/glider_gun.png \ + --preload-file shaders/resources/game_of_life/acorn.png@resources/game_of_life/acorn.png \ + --preload-file shaders/resources/game_of_life/spaceships.png@resources/game_of_life/spaceships.png \ + --preload-file shaders/resources/game_of_life/still_lifes.png@resources/game_of_life/still_lifes.png \ --preload-file shaders/resources/game_of_life/oscillators.png@resources/game_of_life/oscillators.png \ --preload-file shaders/resources/game_of_life/puffer_train.png@resources/game_of_life/puffer_train.png \ - --preload-file shaders/resources/game_of_life/r_pentomino.png@resources/game_of_life/r_pentomino.png \ - --preload-file shaders/resources/game_of_life/spaceships.png@resources/game_of_life/spaceships.png \ - --preload-file shaders/resources/game_of_life/still_lifes.png@resources/game_of_life/still_lifes.png + --preload-file shaders/resources/game_of_life/glider_gun.png@resources/game_of_life/glider_gun.png \ + --preload-file shaders/resources/game_of_life/breeder.png@resources/game_of_life/breeder.png shaders/shaders_hot_reloading: shaders/shaders_hot_reloading.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ diff --git a/examples/README.md b/examples/README.md index 367bfa0ab..d9b03669d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,7 +17,7 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES COLLECTION [TOTAL: 205] +## EXAMPLES COLLECTION [TOTAL: 206] ### category: core [47] @@ -69,11 +69,11 @@ Examples using raylib [core](../src/rcore.c) module platform functionality: wind | [core_directory_files](core/core_directory_files.c) | core_directory_files | ⭐☆☆☆ | 5.5 | 5.6 | [Hugo ARNAL](https://github.com/hugoarnal) | | [core_highdpi_testbed](core/core_highdpi_testbed.c) | core_highdpi_testbed | ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) | | [core_screen_recording](core/core_screen_recording.c) | core_screen_recording | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) | -| [core_clipboard_text](core/core_clipboard_text.c) | core_clipboard_text | ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Ananth S](https://github.com/Ananth1839) | +| [core_clipboard_text](core/core_clipboard_text.c) | core_clipboard_text | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Ananth S](https://github.com/Ananth1839) | | [core_text_file_loading](core/core_text_file_loading.c) | core_text_file_loading | ⭐☆☆☆ | 5.5 | 5.6 | [Aanjishnu Bhattacharyya](https://github.com/NimComPoo-04) | | [core_compute_hash](core/core_compute_hash.c) | core_compute_hash | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) | -### category: shapes [38] +### category: shapes [39] Examples using raylib shapes drawing functionality, provided by raylib [shapes](../src/rshapes.c) module. @@ -117,6 +117,7 @@ Examples using raylib shapes drawing functionality, provided by raylib [shapes]( | [shapes_rlgl_triangle](shapes/shapes_rlgl_triangle.c) | shapes_rlgl_triangle | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Robin](https://github.com/RobinsAviary) | | [shapes_ball_physics](shapes/shapes_ball_physics.c) | shapes_ball_physics | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [David Buzatto](https://github.com/davidbuzatto) | | [shapes_penrose_tile](shapes/shapes_penrose_tile.c) | shapes_penrose_tile | ⭐⭐⭐⭐️ | 5.5 | 5.6-dev | [David Buzatto](https://github.com/davidbuzatto) | +| [shapes_hilbert_curve](shapes/shapes_hilbert_curve.c) | shapes_hilbert_curve | ⭐⭐⭐☆ | 5.6 | 5.6 | [Hamza RAHAL](https://github.com/hmz-rhl) | ### category: textures [29] diff --git a/projects/VS2022/examples/shapes_hilbert_curve.vcxproj b/projects/VS2022/examples/shapes_hilbert_curve.vcxproj new file mode 100644 index 000000000..8fcbfab5f --- /dev/null +++ b/projects/VS2022/examples/shapes_hilbert_curve.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {DC163251-16C3-4B72-B965-ACDBA0F02BD1} + Win32Proj + shapes_hilbert_curve + 10.0 + shapes_hilbert_curve + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\shapes + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 50fef1cf5..df2633843 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -431,6 +431,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_strings_management", " EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_cellular_automata", "examples\textures_cellular_automata.vcxproj", "{0A0FC982-6E31-401F-BA77-3C5E8AB02C68}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_hilbert_curve", "examples\shapes_hilbert_curve.vcxproj", "{DC163251-16C3-4B72-B965-ACDBA0F02BD1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|ARM64 = Debug.DLL|ARM64 @@ -5365,6 +5367,30 @@ Global {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|x64.Build.0 = Release|x64 {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|x86.ActiveCfg = Release|Win32 {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|x86.Build.0 = Release|Win32 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|ARM64.Build.0 = Debug|ARM64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|x64.ActiveCfg = Debug|x64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|x64.Build.0 = Debug|x64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|x86.ActiveCfg = Debug|Win32 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Debug|x86.Build.0 = Debug|Win32 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|ARM64.ActiveCfg = Release|ARM64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|ARM64.Build.0 = Release|ARM64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x64.ActiveCfg = Release|x64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x64.Build.0 = Release|x64 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x86.ActiveCfg = Release|Win32 + {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -5532,7 +5558,7 @@ Global {C54703BF-D68A-480D-BE27-49B62E45D582} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {9CD8BCAD-F212-4BCC-BA98-899743CE3279} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {0981CA28-E4A5-4DF1-987F-A41D09131EFC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {278D8859-20B1-428F-8448-064F46E1F021} {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {6777EC3C-077C-42FC-B4AD-B799CE55CCE4} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {A61DAD9C-271C-4E95-81AA-DB4CD58564D4} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} @@ -5582,6 +5608,7 @@ Global {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC} = {278D8859-20B1-428F-8448-064F46E1F021} {1F4722E7-F78E-413F-A106-D3490211EA57} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {0A0FC982-6E31-401F-BA77-3C5E8AB02C68} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} + {DC163251-16C3-4B72-B965-ACDBA0F02BD1} = {278D8859-20B1-428F-8448-064F46E1F021} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} diff --git a/tools/rexm/reports/examples_issues.md b/tools/rexm/reports/examples_issues.md index 14e7a61c5..081170806 100644 --- a/tools/rexm/reports/examples_issues.md +++ b/tools/rexm/reports/examples_issues.md @@ -21,10 +21,9 @@ Example elements validated: | **EXAMPLE NAME** | [C] | [CAT]| [INFO]|[PNG]|[WPNG]| [RES]| [MK] |[MKWEB]| [VCX]| [SOL]|[RDME]|[JS] | [WOUT]|[WMETA]| |:---------------------------------|:---:|:----:|:-----:|:---:|:----:|:----:|:----:|:-----:|:----:|:----:|:----:|:---:|:-----:|:-----:| | core_highdpi_testbed | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| shaders_game_of_life | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | -| rlgl_standalone | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | -| rlgl_compute_shader | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | -| easings_testbed | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| raylib_opengl_interop | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ❌ | ✔ | ❌ | ✔ | ✔ | -| embedded_files_loading | ✔ | ❌ | ❌ | ✔ | ✔ | ❌ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | -| web_basic_window | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| rlgl_standalone | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| rlgl_compute_shader | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| easings_testbed | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| raylib_opengl_interop | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | +| embedded_files_loading | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| web_basic_window | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | diff --git a/tools/rexm/reports/examples_validation.md b/tools/rexm/reports/examples_validation.md index 45c195415..6770f3c37 100644 --- a/tools/rexm/reports/examples_validation.md +++ b/tools/rexm/reports/examples_validation.md @@ -56,7 +56,7 @@ Example elements validated: | core_smooth_pixelperfect | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_random_sequence | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_automation_events | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| core_high_dpi | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_highdpi_demo | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_render_texture | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_undo_redo | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_viewport_scaling | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -209,7 +209,7 @@ Example elements validated: | shaders_lightmap_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_rounded_rectangle | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shaders_depth_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| shaders_game_of_life | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | +| shaders_game_of_life | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_module_playing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_music_stream | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_raw_stream | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -219,9 +219,10 @@ Example elements validated: | audio_sound_multi | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_sound_positioning | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | audio_spectrum_visualizer | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| rlgl_standalone | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | -| rlgl_compute_shader | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | -| easings_testbed | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| raylib_opengl_interop | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ❌ | ✔ | ❌ | ✔ | ✔ | -| embedded_files_loading | ✔ | ❌ | ❌ | ✔ | ✔ | ❌ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | -| web_basic_window | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| rlgl_standalone | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| rlgl_compute_shader | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| easings_testbed | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| raylib_opengl_interop | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | +| embedded_files_loading | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| web_basic_window | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_hilbert_curve | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | From e534f14419b2c6ab67f4b3a99102d2bb3231f5cb Mon Sep 17 00:00:00 2001 From: CosmosShell Date: Wed, 31 Dec 2025 00:05:39 -0800 Subject: [PATCH 049/117] Fix window width calculation by adding wOffset (#5457) --- src/external/RGFW.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/external/RGFW.h b/src/external/RGFW.h index 7205bf9d8..0ab3858cf 100644 --- a/src/external/RGFW.h +++ b/src/external/RGFW.h @@ -6522,8 +6522,8 @@ LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) if (win->src.aspectRatio.w != 0 && win->src.aspectRatio.h != 0) { double aspectRatio = (double)win->src.aspectRatio.w / win->src.aspectRatio.h; - int width = windowRect.right - windowRect.left; - int height = windowRect.bottom - windowRect.top; + int width = (windowRect.right - windowRect.left) - win->src.wOffset; + int height = (windowRect.bottom - windowRect.top) - win->src.hOffset; int newHeight = (int)(width / aspectRatio); int newWidth = (int)(height * aspectRatio); @@ -6968,6 +6968,7 @@ RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowF DestroyWindow(dummyWin); win->src.hOffset = (u32)(windowRect.bottom - windowRect.top) - (u32)(clientRect.bottom - clientRect.top); + win->src.wOffset = (u32)(windowRect.right - windowRect.left) - (u32)(clientRect.right - clientRect.left); win->src.window = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->r.x, win->r.y, win->r.w, win->r.h + (i32)win->src.hOffset, 0, 0, inh, 0); SetPropW(win->src.window, L"RGFW", win); RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h)); /* so WM_GETMINMAXINFO gets called again */ From 4af95a3a84129c4dc83da45f83ebbb05936e9d4a Mon Sep 17 00:00:00 2001 From: Alvin De Cruz Date: Wed, 31 Dec 2025 17:33:17 +0800 Subject: [PATCH 050/117] Use eglGetPlatformDisplayEXT on DRM platform for Mali compatibility (#5446) --- src/platforms/rcore_drm.c | 50 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 103d81975..ff0118ac5 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -89,6 +89,10 @@ #define EGL_OPENGL_ES3_BIT 0x40 #endif +#ifndef EGL_PLATFORM_GBM_KHR + #define EGL_PLATFORM_GBM_KHR 0x31D7 +#endif + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -1416,7 +1420,30 @@ int InitPlatform(void) EGLint numConfigs = 0; // Get an EGL device connection - platform.device = eglGetDisplay((EGLNativeDisplayType)platform.gbmDevice); + // Try eglGetPlatformDisplayEXT for better compatibility with some drivers (e.g. Mali Midgard) + // REF: https://github.com/raysan5/raylib/issues/5378 + platform.device = EGL_NO_DISPLAY; + const char *eglClientExtensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + + if (eglClientExtensions != NULL) + { + if (strstr(eglClientExtensions, "EGL_EXT_platform_base") != NULL) + { + PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = + (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); + + if (eglGetPlatformDisplayEXT != NULL) + { + platform.device = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, platform.gbmDevice, NULL); + } + } + } + + if (platform.device == EGL_NO_DISPLAY) + { + platform.device = eglGetDisplay((EGLNativeDisplayType)platform.gbmDevice); + } + if (platform.device == EGL_NO_DISPLAY) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); @@ -1496,8 +1523,25 @@ int InitPlatform(void) } // Create an EGL window surface - platform.surface = eglCreateWindowSurface(platform.device, platform.config, (EGLNativeWindowType)platform.gbmSurface, NULL); - if (EGL_NO_SURFACE == platform.surface) + platform.surface = EGL_NO_SURFACE; + + if ((eglClientExtensions != NULL) && (strstr(eglClientExtensions, "EGL_EXT_platform_base") != NULL)) + { + PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT = + (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT"); + + if (eglCreatePlatformWindowSurfaceEXT != NULL) + { + platform.surface = eglCreatePlatformWindowSurfaceEXT(platform.device, platform.config, platform.gbmSurface, NULL); + } + } + + if (platform.surface == EGL_NO_SURFACE) + { + platform.surface = eglCreateWindowSurface(platform.device, platform.config, (EGLNativeWindowType)platform.gbmSurface, NULL); + } + + if (platform.surface == EGL_NO_SURFACE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError()); return -1; From 25ce6465d580652f1149e8698f14b60339230093 Mon Sep 17 00:00:00 2001 From: McDubh <103212704+mcdubhghlas@users.noreply.github.com> Date: Wed, 31 Dec 2025 03:58:58 -0600 Subject: [PATCH 051/117] Added SSE to MatrixMultiply. (#5427) --- src/raymath.h | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/src/raymath.h b/src/raymath.h index 32dfd2b0a..67756a6d0 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -170,6 +170,11 @@ typedef struct float16 { #include // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabsf() +#if defined(__SSE__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 1) + #include + #define RAYMATH_SSE_ENABLED +#endif + //---------------------------------------------------------------------------------- // Module Functions Definition - Utils math //---------------------------------------------------------------------------------- @@ -1647,7 +1652,63 @@ RMAPI Matrix MatrixSubtract(Matrix left, Matrix right) RMAPI Matrix MatrixMultiply(Matrix left, Matrix right) { Matrix result = { 0 }; +#ifdef RAYMATH_SSE_ENABLED + // Load left side and right side. + __m128 c0 = _mm_set_ps(right.m12, right.m8, right.m4, right.m0); + __m128 c1 = _mm_set_ps(right.m13, right.m9, right.m5, right.m1); + __m128 c2 = _mm_set_ps(right.m14, right.m10, right.m6, right.m2); + __m128 c3 = _mm_set_ps(right.m15, right.m11, right.m7, right.m3); + // Transpose so c0..c3 become *rows* of the right matrix in semantic order. + _MM_TRANSPOSE4_PS(c0, c1, c2, c3); + __m128 row; + float tmp[4]; + + // Row 0 of result: [m0, m1, m2, m3] + row = _mm_mul_ps(_mm_set1_ps(left.m0), c0); + row = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m1), c1)); + row = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m2), c2)); + row = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m3), c3)); + _mm_storeu_ps(tmp, row); + result.m0 = tmp[0]; + result.m1 = tmp[1]; + result.m2 = tmp[2]; + result.m3 = tmp[3]; + + // Row 1 of result: [m4, m5, m6, m7] + row = _mm_mul_ps(_mm_set1_ps(left.m4), c0); + row = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m5), c1)); + row = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m6), c2)); + row = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m7), c3)); + _mm_storeu_ps(tmp, row); + result.m4 = tmp[0]; + result.m5 = tmp[1]; + result.m6 = tmp[2]; + result.m7 = tmp[3]; + + // Row 2 of result: [m8, m9, m10, m11] + row = _mm_mul_ps(_mm_set1_ps(left.m8), c0); + row = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m9), c1)); + row = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m10), c2)); + row = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m11), c3)); + _mm_storeu_ps(tmp, row); + result.m8 = tmp[0]; + result.m9 = tmp[1]; + result.m10 = tmp[2]; + result.m11 = tmp[3]; + + // Row 3 of result: [m12, m13, m14, m15] + row = _mm_mul_ps(_mm_set1_ps(left.m12), c0); + row = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m13), c1)); + row = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m14), c2)); + row = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m15), c3)); + _mm_storeu_ps(tmp, row); + result.m12 = tmp[0]; + result.m13 = tmp[1]; + result.m14 = tmp[2]; + result.m15 = tmp[3]; + +#else result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14; @@ -1664,7 +1725,7 @@ RMAPI Matrix MatrixMultiply(Matrix left, Matrix right) result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13; result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; - +#endif return result; } From 02cca28b5f1b9acab403bfdad09605426bb18a23 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 31 Dec 2025 11:08:10 +0100 Subject: [PATCH 052/117] REVIEWED: `eglGetPlatformDisplay()` usage --- src/platforms/rcore_drm.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index ff0118ac5..c5b74b956 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -1420,30 +1420,26 @@ int InitPlatform(void) EGLint numConfigs = 0; // Get an EGL device connection - // Try eglGetPlatformDisplayEXT for better compatibility with some drivers (e.g. Mali Midgard) - // REF: https://github.com/raysan5/raylib/issues/5378 + // NOTE: eglGetPlatformDisplay() is preferred over eglGetDisplay() legacy call platform.device = EGL_NO_DISPLAY; +#if defined(EGL_VERSION_1_5) + platform.device = eglGetPlatformDisplay(EGL_PLATFORM_GBM_KHR, platform.gbmDevice, NULL); +#else + // Check if extension is available for eglGetPlatformDisplayEXT() + // NOTE: Better compatibility with some drivers (e.g. Mali Midgard) const char *eglClientExtensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); - if (eglClientExtensions != NULL) { if (strstr(eglClientExtensions, "EGL_EXT_platform_base") != NULL) { - PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = - (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); - - if (eglGetPlatformDisplayEXT != NULL) - { - platform.device = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, platform.gbmDevice, NULL); - } + PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); + if (eglGetPlatformDisplayEXT != NULL) platform.device = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, platform.gbmDevice, NULL); } } - if (platform.device == EGL_NO_DISPLAY) - { - platform.device = eglGetDisplay((EGLNativeDisplayType)platform.gbmDevice); - } - + // In case extension not found or display could not be retrieved, try useing legacy version + if (platform.device == EGL_NO_DISPLAY) platform.device = eglGetDisplay((EGLNativeDisplayType)platform.gbmDevice); +#endif if (platform.device == EGL_NO_DISPLAY) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); From 66755da4c8f6ae2f2be9accc1563077756f03dc7 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 31 Dec 2025 11:08:17 +0100 Subject: [PATCH 053/117] REVIEWED: `eglGetPlatformDisplay()` usage --- src/platforms/rcore_android.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index cc012c4fe..e1dba72c8 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -919,7 +919,27 @@ static int InitGraphicsDevice(void) EGLint numConfigs = 0; // Get an EGL device connection - platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY); + // NOTE: eglGetPlatformDisplay() is preferred over eglGetDisplay() legacy call + platform.device = EGL_NO_DISPLAY; +#if defined(EGL_VERSION_1_5) + platform.device = eglGetPlatformDisplay(EGL_PLATFORM_GBM_KHR, platform.gbmDevice, NULL); +#else + // Check if extension is available for eglGetPlatformDisplayEXT() + // NOTE: Better compatibility with some drivers (e.g. Mali Midgard) + const char *eglClientExtensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + if (eglClientExtensions != NULL) + { + if (strstr(eglClientExtensions, "EGL_EXT_platform_base") != NULL) + { + PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); + if (eglGetPlatformDisplayEXT != NULL) platform.device = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, platform.gbmDevice, NULL); + } + } + + // In case extension not found or display could not be retrieved, try useing legacy version + if (platform.device == EGL_NO_DISPLAY) platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY); +#endif + if (platform.device == EGL_NO_DISPLAY) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); From c124f2552bbdcd9030e57f22283f5dd6570bf484 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 31 Dec 2025 11:22:26 +0100 Subject: [PATCH 054/117] REVIEWED: SIMD instrinsics must be explicitly enabled by developer, only SSE supported at the moment #5316 --- src/raymath.h | 60 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/src/raymath.h b/src/raymath.h index 67756a6d0..8d5b1b2a9 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -19,17 +19,22 @@ * * CONFIGURATION: * #define RAYMATH_IMPLEMENTATION -* Generates the implementation of the library into the included file. +* Generates the implementation of the library into the included file * If not defined, the library is in header only mode and can be included in other headers -* or source files without problems. But only ONE file should hold the implementation. +* or source files without problems. But only ONE file should hold the implementation * * #define RAYMATH_STATIC_INLINE -* Define static inline functions code, so #include header suffices for use. -* This may use up lots of memory. +* Define static inline functions code, so #include header suffices for use +* This may use up lots of memory * * #define RAYMATH_DISABLE_CPP_OPERATORS * Disables C++ operator overloads for raymath types. * +* #define RAYMATH_USE_SIMD_INTRINSICS +* Try to enable SIMD intrinsics for MatrixMultiply() +* Note that users enabling it must be aware of the target platform where application will +* run to support the selected SIMD intrinsic, for now, only SSE is supported +* * LICENSE: zlib/libpng * * Copyright (c) 2015-2025 Ramon Santamaria (@raysan5) @@ -79,7 +84,6 @@ #endif #endif - //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -170,9 +174,35 @@ typedef struct float16 { #include // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabsf() -#if defined(__SSE__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 1) - #include - #define RAYMATH_SSE_ENABLED +#if defined(RAYMATH_USE_SIMD_INTRINSICS) + // SIMD is used on the most costly raymath function MatrixMultiply() + // NOTE: Only SSE intrinsics support implemented + // TODO: Consider support for other SIMD instrinsics + /* + #if defined(__SSE4_2__) + #define SW_HAS_SSE42 + #include + #elif defined(__SSE4_1__) + #define SW_HAS_SSE41 + #include + #elif defined(__SSSE3__) + #define SW_HAS_SSSE3 + #include + #elif defined(__SSE3__) + #define SW_HAS_SSE3 + #include + #elif defined(__SSE2__) || (defined(_M_AMD64) || defined(_M_X64)) // SSE2 x64 + #define SW_HAS_SSE2 + #include + #elif defined(__SSE__) + #define SW_HAS_SSE + #include + #endif + */ + #if defined(__SSE__) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP >= 1)) + #include + #define RAYMATH_SSE_ENABLED + #endif #endif //---------------------------------------------------------------------------------- @@ -1652,18 +1682,20 @@ RMAPI Matrix MatrixSubtract(Matrix left, Matrix right) RMAPI Matrix MatrixMultiply(Matrix left, Matrix right) { Matrix result = { 0 }; -#ifdef RAYMATH_SSE_ENABLED - // Load left side and right side. + +#if defined(RAYMATH_SSE_ENABLED) + // Load left side and right side __m128 c0 = _mm_set_ps(right.m12, right.m8, right.m4, right.m0); __m128 c1 = _mm_set_ps(right.m13, right.m9, right.m5, right.m1); __m128 c2 = _mm_set_ps(right.m14, right.m10, right.m6, right.m2); __m128 c3 = _mm_set_ps(right.m15, right.m11, right.m7, right.m3); - // Transpose so c0..c3 become *rows* of the right matrix in semantic order. + + // Transpose so c0..c3 become *rows* of the right matrix in semantic order _MM_TRANSPOSE4_PS(c0, c1, c2, c3); + float tmp[4] = { 0 }; __m128 row; - float tmp[4]; - + // Row 0 of result: [m0, m1, m2, m3] row = _mm_mul_ps(_mm_set1_ps(left.m0), c0); row = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m1), c1)); @@ -1707,7 +1739,6 @@ RMAPI Matrix MatrixMultiply(Matrix left, Matrix right) result.m13 = tmp[1]; result.m14 = tmp[2]; result.m15 = tmp[3]; - #else result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; @@ -1726,6 +1757,7 @@ RMAPI Matrix MatrixMultiply(Matrix left, Matrix right) result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; #endif + return result; } From 0133a4e6c6966e71567ecd663a1cdf43413042f5 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Wed, 31 Dec 2025 11:45:29 -0800 Subject: [PATCH 055/117] Make CameraMove up and right work with Z up cameras like the other functions do. (#5458) --- src/rcamera.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index 3e9f83095..d67669a7b 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -252,8 +252,13 @@ void CameraMoveForward(Camera *camera, float distance, bool moveInWorldPlane) if (moveInWorldPlane) { - // Project vector onto world plane - forward.y = 0; + // Project vector onto world plane (the plane defined by the up vector) + if (fabsf(camera->up.z) > 0) + forward.z = 0; + else if (fabsf(camera->up.x) > 0) + forward.x = 0; + else + forward.y = 0; forward = Vector3Normalize(forward); } @@ -285,8 +290,14 @@ void CameraMoveRight(Camera *camera, float distance, bool moveInWorldPlane) if (moveInWorldPlane) { - // Project vector onto world plane - right.y = 0; + // Project vector onto world plane (the plane defined by the up vector) + if (fabsf(camera->up.z) > 0) + right.z = 0; + else if (fabsf(camera->up.x) > 0) + right.x = 0; + else + right.y = 0; + right = Vector3Normalize(right); } From 2377506843c9d397cdc5123841730ae8e53a5d84 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 31 Dec 2025 20:50:27 +0100 Subject: [PATCH 056/117] Update rcamera.h --- src/rcamera.h | 51 ++++++++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index d67669a7b..12f3a9e09 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -50,14 +50,14 @@ // Function specifiers in case library is build/used as a shared library (Windows) // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll #if defined(_WIN32) -#if defined(BUILD_LIBTYPE_SHARED) -#if defined(__TINYC__) -#define __declspec(x) __attribute__((x)) -#endif -#define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) -#elif defined(USE_LIBTYPE_SHARED) -#define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) -#endif + #if defined(BUILD_LIBTYPE_SHARED) + #if defined(__TINYC__) + #define __declspec(x) __attribute__((x)) + #endif + #define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) + #elif defined(USE_LIBTYPE_SHARED) + #define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) + #endif #endif #ifndef RLAPI @@ -191,19 +191,21 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera *camera, float aspect); // IsKeyDown() // IsKeyPressed() // GetFrameTime() + +#include // Required for: fabsf() //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define CAMERA_MOVE_SPEED 5.4f // Units per second -#define CAMERA_ROTATION_SPEED 0.03f -#define CAMERA_PAN_SPEED 0.2f +#define CAMERA_MOVE_SPEED 5.4f // Units per second +#define CAMERA_ROTATION_SPEED 0.03f +#define CAMERA_PAN_SPEED 0.2f // Camera mouse movement sensitivity -#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f +#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f // Camera orbital speed in CAMERA_ORBITAL mode -#define CAMERA_ORBITAL_SPEED 0.5f // Radians per second +#define CAMERA_ORBITAL_SPEED 0.5f // Radians per second //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -253,12 +255,10 @@ void CameraMoveForward(Camera *camera, float distance, bool moveInWorldPlane) if (moveInWorldPlane) { // Project vector onto world plane (the plane defined by the up vector) - if (fabsf(camera->up.z) > 0) - forward.z = 0; - else if (fabsf(camera->up.x) > 0) - forward.x = 0; - else - forward.y = 0; + if (fabsf(camera->up.z) > 0) forward.z = 0; + else if (fabsf(camera->up.x) > 0) forward.x = 0; + else forward.y = 0; + forward = Vector3Normalize(forward); } @@ -291,12 +291,9 @@ void CameraMoveRight(Camera *camera, float distance, bool moveInWorldPlane) if (moveInWorldPlane) { // Project vector onto world plane (the plane defined by the up vector) - if (fabsf(camera->up.z) > 0) - right.z = 0; - else if (fabsf(camera->up.x) > 0) - right.x = 0; - else - right.y = 0; + if (fabsf(camera->up.z) > 0) right.z = 0; + else if (fabsf(camera->up.x) > 0) right.x = 0; + else right.y = 0; right = Vector3Normalize(right); } @@ -356,7 +353,7 @@ void CameraYaw(Camera *camera, float angle, bool rotateAroundTarget) // - lockView prevents camera overrotation (aka "somersaults") // - rotateAroundTarget defines if rotation is around target or around its position // - rotateUp rotates the up direction as well (typically only usefull in CAMERA_FREE) -// NOTE: angle must be provided in radians +// NOTE: [angle] must be provided in radians void CameraPitch(Camera *camera, float angle, bool lockView, bool rotateAroundTarget, bool rotateUp) { // Up direction @@ -393,7 +390,7 @@ void CameraPitch(Camera *camera, float angle, bool lockView, bool rotateAroundTa // Move position relative to target camera->position = Vector3Subtract(camera->target, targetPosition); } - else // rotate around camera.position + else // Rotate around camera.position { // Move target relative to position camera->target = Vector3Add(camera->position, targetPosition); From 83377a34884dd85ce6b72bf3b50247585e0bc843 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 31 Dec 2025 22:29:12 +0100 Subject: [PATCH 057/117] Update examples_list.txt --- examples/examples_list.txt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/examples_list.txt b/examples/examples_list.txt index 3310cf2d2..96d64ca84 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -1,11 +1,13 @@ # -# raylib examples list used to generate/update collection -# examples must be provided as: ;;;;;;;""; +# raylib examples list with available .c example files +# +# WARNING: List is not ordered by example name but by the display order on web, +# so it can not be automatically generated scanning available .c code files, only updated +# new examples are added at the end of each category; it's up to the user to reorder them as desired +# +# examples data is listed as: ;;;;;;;""; # # This list is used as the main reference by [rexm] tool for examples collection validation and management -# New examples must be added to this list and any possible rename must be made on this list first -# -# WARNING: List is not ordered by example name but by the display order on web # core;core_basic_window;★☆☆☆;1.0;1.0;2013;2025;"Ramon Santamaria";@raysan5 core;core_delta_time;★☆☆☆;5.5;5.6-dev;2025;2025;"Robin";@RobinsAviary @@ -92,6 +94,7 @@ shapes;shapes_rlgl_color_wheel;★★★☆;5.6-dev;5.6-dev;2025;2025;"Robin";@R shapes;shapes_rlgl_triangle;★★☆☆;5.6-dev;5.6-dev;2025;2025;"Robin";@RobinsAviary shapes;shapes_ball_physics;★★☆☆;5.6-dev;5.6-dev;2025;2025;"David Buzatto";@davidbuzatto shapes;shapes_penrose_tile;★★★★;5.5;5.6-dev;2025;2025;"David Buzatto";@davidbuzatto +shapes;shapes_hilbert_curve;★★★☆;5.6;5.6;2025;2025;"Hamza RAHAL";@hmz-rhl textures;textures_logo_raylib;★☆☆☆;1.0;1.0;2014;2025;"Ramon Santamaria";@raysan5 textures;textures_srcrec_dstrec;★★★☆;1.3;1.3;2015;2025;"Ramon Santamaria";@raysan5 textures;textures_image_drawing;★★☆☆;1.4;1.4;2016;2025;"Ramon Santamaria";@raysan5 From f805e6cae82b945ff02c2381b8ab6d99e6a8f8cb Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 31 Dec 2025 22:46:36 +0100 Subject: [PATCH 058/117] REXM: Update `Makefile.Web` before trying to rebuild new example for web --- tools/rexm/rexm.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index aa491ff23..10b91e82f 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -1226,6 +1226,18 @@ int main(int argc, char *argv[]) // Actions to fix/review anything possible from validation results //------------------------------------------------------------------------------------------------ + // Update files: Makefile, Makefile.Web, README.md, examples.js + // Solves: VALID_NOT_IN_MAKEFILE, VALID_NOT_IN_MAKEFILE_WEB, VALID_NOT_IN_README, VALID_NOT_IN_JS + // WARNING: Makefile.Web needs to be updated before trying to rebuild web example! + UpdateRequiredFiles(); + for (int i = 0; i < exCollectionCount; i++) + { + exCollection[i].status &= ~VALID_NOT_IN_MAKEFILE; + exCollection[i].status &= ~VALID_NOT_IN_MAKEFILE_WEB; + exCollection[i].status &= ~VALID_NOT_IN_README; + exCollection[i].status &= ~VALID_NOT_IN_JS; + } + // Check examples "status" information for (int i = 0; i < exCollectionCount; i++) { @@ -1325,17 +1337,6 @@ int main(int argc, char *argv[]) } } } - - // Update files: Makefile, Makefile.Web, README.md, examples.js - // Solves: VALID_NOT_IN_MAKEFILE, VALID_NOT_IN_MAKEFILE_WEB, VALID_NOT_IN_README, VALID_NOT_IN_JS - UpdateRequiredFiles(); - for (int i = 0; i < exCollectionCount; i++) - { - exCollection[i].status &= ~VALID_NOT_IN_MAKEFILE; - exCollection[i].status &= ~VALID_NOT_IN_MAKEFILE_WEB; - exCollection[i].status &= ~VALID_NOT_IN_README; - exCollection[i].status &= ~VALID_NOT_IN_JS; - } //------------------------------------------------------------------------------------------------ } From ab1d9b38304ff30b6207565e4038e11200c6cbbd Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 31 Dec 2025 22:47:16 +0100 Subject: [PATCH 059/117] REXM: Check example exists (compilation worked) before trying to run it --- tools/rexm/rexm.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 10b91e82f..aab316503 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -1592,11 +1592,16 @@ int main(int argc, char *argv[]) FileRemove(TextFormat("%s/%s/%s.original.c", exBasePath, exCategory, exName)); // STEP 3: Run example on browser - // WARNING: Example download is asynchronous so reading fails on next step - // when looking for a file that could not have been downloaded yet - ChangeDirectory(TextFormat("%s", exBasePath)); - if (i == 0) system("start python -m http.server 8080"); // Init localhost just once - system(TextFormat("start explorer \"http:\\localhost:8080/%s/%s.html", exCategory, exName)); + if (FileExists(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName)) && + FileExists(TextFormat("%s/%s/%s.wasm", exBasePath, exCategory, exName)) && + FileExists(TextFormat("%s/%s/%s.js", exBasePath, exCategory, exName))) + { + // WARNING: Example download is asynchronous so reading fails on next step + // when looking for a file that could not have been downloaded yet + ChangeDirectory(TextFormat("%s", exBasePath)); + if (i == 0) system("start python -m http.server 8080"); // Init localhost just once + system(TextFormat("start explorer \"http:\\localhost:8080/%s/%s.html", exCategory, exName)); + } // NOTE: Example .log is automatically downloaded into system Downloads directory on browser-example exectution From cac02ab0639cdd70b99031c0f3ac98176de7c4b1 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 31 Dec 2025 22:48:07 +0100 Subject: [PATCH 060/117] REXM: REVIEWED: Add new example to collection list at the end of its category, instead of adding it at the end of the file --- tools/rexm/rexm.c | 115 ++++++++++++++++++++++++++-------------------- 1 file changed, 66 insertions(+), 49 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index aab316503..36152e560 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -207,8 +207,8 @@ static void UpdateSourceMetadata(const char *exSrcPath, const rlExampleInfo *inf // Update generated Web example .html file metadata static void UpdateWebMetadata(const char *exHtmlPath, const char *exFilePath); -// Check if text string is a list of strings -static bool TextInList(const char *text, const char **list, int listCount); +// Check if text string is in a list of strings and get index, -1 if not found +static int GetTextListIndex(const char *text, const char **list, int listCount); //------------------------------------------------------------------------------------ // Program main entry point @@ -1003,6 +1003,9 @@ int main(int argc, char *argv[]) VALID_INVALID_CATEGORY */ + // Validate and update examples collection list + // NOTE: New .c examples found are added at the end of its category + //--------------------------------------------------------------------------------------------------- // Scan available example .c files and add to collection missing ones // NOTE: Source of truth is what we have in the examples directories (on validation/update) LOG("INFO: Scanning available example (.c) files to be added to collection...\n"); @@ -1010,14 +1013,66 @@ int main(int argc, char *argv[]) // Load examples collection list file (raylib/examples/examples_list.txt) char *exList = LoadFileText(exCollectionFilePath); + int exListLen = (int)strlen(exList); + char *exListUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1); bool listUpdated = false; - int exListLen = (int)strlen(exList); - strcpy(exListUpdated, exList); + // Add new examples to the collection list if not found + // WARNING: Added to the end of category, order defines place on raylib webpage + for (unsigned int i = 0; i < clist.count; i++) + { + // NOTE: Skipping "examples_template" from checks + if (!TextIsEqual(GetFileNameWithoutExt(clist.paths[i]), "examples_template") && + (TextFindIndex(exList, GetFileNameWithoutExt(clist.paths[i])) == -1)) + { + // Get new example data + rlExampleInfo *exInfo = LoadExampleInfo(clist.paths[i]); - // Copy examples list into an update list - // NOTE: Checking and removing duplicate entries + // Get example category, -1 if not found in list + int catIndex = GetTextListIndex(exInfo->category, exCategories, REXM_MAX_EXAMPLE_CATEGORIES); + + if (catIndex > -1) + { + int nextCatIndex = catIndex + 1; + if (nextCatIndex > (REXM_MAX_EXAMPLE_CATEGORIES - 1)) nextCatIndex = -1; // EOF + + // Find position to add new example on list, just before the following category + // Category order: core, shapes, textures, text, models, shaders, audio, [others] + int exListNextCatIndex = -1; + if (nextCatIndex != -1) exListNextCatIndex = TextFindIndex(exList, exCategories[nextCatIndex]); + else exListNextCatIndex = exListLen; // EOF + + strncpy(exListUpdated, exList, exListNextCatIndex); + + // Get example difficulty stars + char starsText[16] = { 0 }; + for (int s = 0; s < 4; s++) + { + // NOTE: Every UTF-8 star are 3 bytes + if (s < exInfo->stars) strcpy(starsText + 3*s, "★"); + else strcpy(starsText + 3*s, "☆"); + } + + // Add new example to the list + int exListNewExLen = sprintf(exListUpdated + exListNextCatIndex, + TextFormat("%s;%s;%s;%s;%s;%i;%i;\"%s\";@%s\n", + exInfo->category, exInfo->name, starsText, exInfo->verCreated, + exInfo->verUpdated, exInfo->yearCreated, exInfo->yearReviewed, + exInfo->author, exInfo->authorGitHub)); + + // Add the following examples to the end of collection list + strncpy(exListUpdated + exListNextCatIndex + exListNewExLen, exList + exListNextCatIndex, exListLen - exListNextCatIndex); + + listUpdated = true; + } + + UnloadExampleInfo(exInfo); + } + } + + /* + // Check and remove duplicate example entries int lineCount = 0; char **exListLines = LoadTextLines(exList, &lineCount); int exListUpdatedOffset = 0; @@ -1031,46 +1086,7 @@ int main(int argc, char *argv[]) } UnloadTextLines(exListLines, lineCount); - - for (unsigned int i = 0; i < clist.count; i++) - { - // NOTE: Skipping "examples_template" from checks - if (!TextIsEqual(GetFileNameWithoutExt(clist.paths[i]), "examples_template") && - (TextFindIndex(exList, GetFileNameWithoutExt(clist.paths[i])) == -1)) - { - // TODO: Examples to be added in the list should be added at the end of their categories, - // not at the end of the file... - - // Add example to the examples collection list - // WARNING: Added to the end of the list, order must be set by users and - // defines placement on raylib webpage - rlExampleInfo *exInfo = LoadExampleInfo(clist.paths[i]); - - // Validate example category - // TODO: Should [others] category be considered? - if (TextInList(exInfo->category, exCategories, REXM_MAX_EXAMPLE_CATEGORIES))// && !TextIsEqual(exInfo->category, "others")) - { - // Get example difficulty stars - char starsText[16] = { 0 }; - for (int s = 0; s < 4; s++) - { - // NOTE: Every UTF-8 star are 3 bytes - if (s < exInfo->stars) strcpy(starsText + 3*s, "★"); - else strcpy(starsText + 3*s, "☆"); - } - - exListLen += sprintf(exListUpdated + exListLen, - TextFormat("%s;%s;%s;%s;%s;%i;%i;\"%s\";@%s\n", - exInfo->category, exInfo->name, starsText, exInfo->verCreated, - exInfo->verUpdated, exInfo->yearCreated, exInfo->yearReviewed, - exInfo->author, exInfo->authorGitHub)); - - listUpdated = true; - } - - UnloadExampleInfo(exInfo); - } - } + */ if (listUpdated) SaveFileText(exCollectionFilePath, exListUpdated); @@ -1078,6 +1094,7 @@ int main(int argc, char *argv[]) RL_FREE(exListUpdated); UnloadDirectoryFiles(clist); + //--------------------------------------------------------------------------------------------------- // Check all examples in collection [examples_list.txt] -> Source of truth! LOG("INFO: Validating examples in collection...\n"); @@ -2918,13 +2935,13 @@ static void UpdateWebMetadata(const char *exHtmlPath, const char *exFilePath) } // Check if text string is a list of strings -static bool TextInList(const char *text, const char **list, int listCount) +static int GetTextListIndex(const char *text, const char **list, int listCount) { - bool result = false; + int result = -1; for (int i = 0; i < listCount; i++) { - if (TextIsEqual(text, list[i])) { result = true; break; } + if (TextIsEqual(text, list[i])) { result = i; break; } } return result; From 95f72b162b7041ad699b59b0fdaf65e6424a26a1 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 31 Dec 2025 22:50:17 +0100 Subject: [PATCH 061/117] REVIEWED: `TextReplace()`, revert breaking change, needs to be reviewed again... -WIP- --- src/rtext.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 453ed4507..b0f77a767 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1746,16 +1746,23 @@ char *TextReplace(const char *text, const char *search, const char *replacement) { insertPoint = (char *)strstr(text, search); lastReplacePos = (int)(insertPoint - text); - temp = strncpy(temp, text, tempLen - 1) + lastReplacePos; - tempLen -= lastReplacePos; - temp = strncpy(temp, replacement, tempLen - 1) + replaceLen; - tempLen -= replaceLen; + + // TODO: Review logic to avoid strcpy() + // OK - Those lines work + temp = strncpy(temp, text, lastReplacePos) + lastReplacePos; + temp = strcpy(temp, replacement) + replaceLen; + // WRONG - But not those ones + //temp = strncpy(temp, text, tempLen - 1) + lastReplacePos; + //tempLen -= lastReplacePos; + //temp = strncpy(temp, replacement, tempLen - 1) + replaceLen; + //tempLen -= replaceLen; text += lastReplacePos + searchLen; // Move to next "end of replace" } // Copy remaind text part after replacement to result (pointed by moving temp) - strncpy(temp, text, tempLen - 1); + strcpy(temp, text); // OK + //strncpy(temp, text, tempLen - 1); // WRONG } return result; From eb4ad50d9904ff0359e303708127d3d9ba68dab2 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Wed, 31 Dec 2025 14:52:08 -0800 Subject: [PATCH 062/117] make sure that our up vector really is up in an axis before picking a world plane (#5459) --- src/rcamera.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index 12f3a9e09..72552ec15 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -255,8 +255,8 @@ void CameraMoveForward(Camera *camera, float distance, bool moveInWorldPlane) if (moveInWorldPlane) { // Project vector onto world plane (the plane defined by the up vector) - if (fabsf(camera->up.z) > 0) forward.z = 0; - else if (fabsf(camera->up.x) > 0) forward.x = 0; + if (fabsf(camera->up.z) > 0.7071f) forward.z = 0; + else if (fabsf(camera->up.x) > 0.7071f) forward.x = 0; else forward.y = 0; forward = Vector3Normalize(forward); @@ -291,8 +291,8 @@ void CameraMoveRight(Camera *camera, float distance, bool moveInWorldPlane) if (moveInWorldPlane) { // Project vector onto world plane (the plane defined by the up vector) - if (fabsf(camera->up.z) > 0) right.z = 0; - else if (fabsf(camera->up.x) > 0) right.x = 0; + if (fabsf(camera->up.z) > 0.7071f) right.z = 0; + else if (fabsf(camera->up.x) > 0.7071f) right.x = 0; else right.y = 0; right = Vector3Normalize(right); From 909f040dc5038fdb7b275481d8766bf8c7239346 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 1 Jan 2026 16:33:34 +0100 Subject: [PATCH 063/117] Remove trailing spaces --- src/platforms/rcore_android.c | 2 +- src/platforms/rcore_desktop_glfw.c | 20 +++++----- src/platforms/rcore_desktop_rgfw.c | 2 +- src/platforms/rcore_desktop_sdl.c | 6 +-- src/platforms/rcore_desktop_win32.c | 12 +++--- src/platforms/rcore_drm.c | 48 ++++++++++++------------ src/platforms/rcore_memory.c | 4 +- src/platforms/rcore_web.c | 16 ++++---- src/platforms/rcore_web_emscripten.c | 56 ++++++++++++++-------------- src/raudio.c | 4 +- src/rcore.c | 20 +++++----- src/rlgl.h | 2 +- src/rmodels.c | 4 +- src/rtext.c | 16 ++++---- src/rtextures.c | 4 +- 15 files changed, 108 insertions(+), 108 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index e1dba72c8..4f36f0a5b 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -360,7 +360,7 @@ void RestoreWindow(void) void SetWindowState(unsigned int flags) { if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead"); - + // State change: FLAG_WINDOW_ALWAYS_RUN if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN); } diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index a1c0024aa..24b6d8d18 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -188,7 +188,7 @@ void ToggleFullscreen(void) GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); GLFWmonitor *monitor = (monitorIndex < monitorCount)? monitors[monitorIndex] : NULL; - if (monitor != NULL) + if (monitor != NULL) { // Get current monitor video mode const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitorIndex]); @@ -233,7 +233,7 @@ void ToggleFullscreen(void) #endif // WARNING: This function launches FramebufferSizeCallback() - glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, + glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); #if defined(_GLFW_X11) || defined(_GLFW_WAYLAND) @@ -283,7 +283,7 @@ void ToggleBorderlessWindowed(void) CORE.Window.screen.height = mode->height; // Set screen position and size - glfwSetWindowMonitor(platform.handle, monitors[monitor], CORE.Window.position.x, CORE.Window.position.y, + glfwSetWindowMonitor(platform.handle, monitors[monitor], CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, mode->refreshRate); // Refocus window @@ -312,7 +312,7 @@ void ToggleBorderlessWindowed(void) #endif // Return to previous screen size and position - glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, + glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, mode->refreshRate); // Refocus window @@ -908,7 +908,7 @@ Vector2 GetMonitorPosition(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) { - int x = 0; + int x = 0; int y = 0; glfwGetMonitorPos(monitors[monitor], &x, &y); @@ -1026,7 +1026,7 @@ Vector2 GetWindowPosition(void) Vector2 GetWindowScaleDPI(void) { Vector2 scale = { 1.0f, 1.0f }; - if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI) && !FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) + if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI) && !FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) glfwGetWindowContentScale(platform.handle, &scale.x, &scale.y); return scale; } @@ -1275,7 +1275,7 @@ void PollInputEvents(void) state.axes[GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f; state.axes[GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f; } - + const unsigned char *buttons = state.buttons; for (int k = 0; (buttons != NULL) && (k < MAX_GAMEPAD_BUTTONS); k++) @@ -1345,7 +1345,7 @@ void PollInputEvents(void) CORE.Window.resizedLastFrame = false; - if ((CORE.Window.eventWaiting) || + if ((CORE.Window.eventWaiting) || (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED) && !FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN))) { glfwWaitEvents(); // Wait for in input events before continue (drawing is paused) @@ -1455,7 +1455,7 @@ int InitPlatform(void) glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE); #endif // Resize window content area based on the monitor content scale - // NOTE: This hint only has an effect on platforms where screen coordinates and + // NOTE: This hint only has an effect on platforms where screen coordinates and // pixels always map 1:1 such as Windows and X11 // On platforms like macOS the resolution of the framebuffer is changed independently of the window size glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); @@ -1463,7 +1463,7 @@ int InitPlatform(void) glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE); #endif } - else + else { glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE); #if defined(__APPLE__) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 558b6de55..b906fd103 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -330,7 +330,7 @@ void ToggleFullscreen(void) void ToggleBorderlessWindowed(void) { if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) ToggleFullscreen(); - + if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) { CORE.Window.previousPosition = CORE.Window.position; diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 952268ca6..cc3f7c6ae 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1425,8 +1425,8 @@ void PollInputEvents(void) #if defined(USING_VERSION_SDL3) // const char *data; // The text for SDL_EVENT_DROP_TEXT and the file name for SDL_EVENT_DROP_FILE, NULL for other events - // Event memory is now managed by SDL, so you should not free the data in SDL_EVENT_DROP_FILE, - // and if you want to hold onto the text in SDL_EVENT_TEXT_EDITING and SDL_EVENT_TEXT_INPUT events, + // Event memory is now managed by SDL, so you should not free the data in SDL_EVENT_DROP_FILE, + // and if you want to hold onto the text in SDL_EVENT_TEXT_EDITING and SDL_EVENT_TEXT_INPUT events, // you should make a copy of it. SDL_TEXTINPUTEVENT_TEXT_SIZE is no longer necessary and has been removed strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data, MAX_FILEPATH_LENGTH - 1); #else @@ -1487,7 +1487,7 @@ void PollInputEvents(void) CORE.Window.resizedLastFrame = true; #ifndef USING_VERSION_SDL3 - // Manually detect if the window was maximized (due to SDL2 restore being unreliable on some platforms) + // Manually detect if the window was maximized (due to SDL2 restore being unreliable on some platforms) // to remove the FLAG_WINDOW_MAXIMIZED accordingly if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) { diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 973fafa68..8d9bc6c8b 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -263,7 +263,7 @@ static bool DecoratedFromStyle(DWORD style) static DWORD MakeWindowStyle(unsigned flags) { // Flag is not needed because there are no child windows, - // but supposedly it improves efficiency, plus, windows adds this + // but supposedly it improves efficiency, plus, windows adds this // flag automatically anyway so it keeps flags in sync with the OS DWORD style = WS_CLIPSIBLINGS; @@ -1880,19 +1880,19 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara // Get current dpi scale factor float scalex = HIWORD(wParam)/96.0f; float scaley = LOWORD(wParam)/96.0f; - + RECT *suggestedRect = (RECT *)lparam; // Never set the window size to anything other than the suggested rect here // Doing so can cause a window to stutter between monitors when transitioning between them - int result = (int)SetWindowPos(hwnd, NULL, + int result = (int)SetWindowPos(hwnd, NULL, suggestedRect->left, suggestedRect->top, - suggestedRect->right - suggestedRect->left, - suggestedRect->bottom - suggestedRect->top, + suggestedRect->right - suggestedRect->left, + suggestedRect->bottom - suggestedRect->top, SWP_NOZORDER | SWP_NOACTIVATE); if (result == 0) TRACELOG(LOG_ERROR, "Failed to set window position [ERROR: %lu]", GetLastError()); - + // TODO: Update screen data, render size, screen scaling, viewport... } break; diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index c5b74b956..8db332b06 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -1606,10 +1606,10 @@ int InitPlatform(void) if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow(); // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) - { - TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); - return -1; + if (!CORE.Window.ready) + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); + return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); @@ -1933,7 +1933,7 @@ static void InitEvdevInput(void) platform.touchPosition[i].y = -1; platform.touchId[i] = -1; } - + // Initialize touch slot platform.touchSlot = 0; @@ -2116,13 +2116,13 @@ static void ConfigureEvdevDevice(char *device) if (prioritize) { deviceKindStr = isTouch? "touchscreen" : "mouse"; - - if (platform.mouseFd != -1) + + if (platform.mouseFd != -1) { TRACELOG(LOG_INFO, "INPUT: Overwriting previous input device with new %s", deviceKindStr); close(platform.mouseFd); } - + platform.mouseFd = fd; platform.mouseIsTouch = isTouch; @@ -2134,7 +2134,7 @@ static void ConfigureEvdevDevice(char *device) platform.absRange.y = absinfo[ABS_Y].info.minimum; platform.absRange.height = absinfo[ABS_Y].info.maximum - absinfo[ABS_Y].info.minimum; } - + TRACELOG(LOG_INFO, "INPUT: Initialized input device %s as %s", device, deviceKindStr); } else @@ -2357,9 +2357,9 @@ static void PollMouseEvents(void) if (event.code == ABS_X) { CORE.Input.Mouse.currentPosition.x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; // Scale according to absRange - + // Update single touch position only if it's active and no MT events are being used - if (platform.touchActive[0] && !isMultitouch) + if (platform.touchActive[0] && !isMultitouch) { platform.touchPosition[0].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; if (touchAction == -1) touchAction = 2; // TOUCH_ACTION_MOVE @@ -2369,9 +2369,9 @@ static void PollMouseEvents(void) if (event.code == ABS_Y) { CORE.Input.Mouse.currentPosition.y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; // Scale according to absRange - + // Update single touch position only if it's active and no MT events are being used - if (platform.touchActive[0] && !isMultitouch) + if (platform.touchActive[0] && !isMultitouch) { platform.touchPosition[0].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; if (touchAction == -1) touchAction = 2; // TOUCH_ACTION_MOVE @@ -2379,9 +2379,9 @@ static void PollMouseEvents(void) } // Multitouch movement - if (event.code == ABS_MT_SLOT) + if (event.code == ABS_MT_SLOT) { - platform.touchSlot = event.value; + platform.touchSlot = event.value; isMultitouch = true; } @@ -2391,7 +2391,7 @@ static void PollMouseEvents(void) if (platform.touchSlot < MAX_TOUCH_POINTS) { platform.touchPosition[platform.touchSlot].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; - + // If this slot is active, it's a move. If not, we are just updating the buffer for when it becomes active. // Only set to MOVE if we haven't already detected a DOWN or UP event this frame if (platform.touchActive[platform.touchSlot] && touchAction == -1) touchAction = 2; // TOUCH_ACTION_MOVE @@ -2403,7 +2403,7 @@ static void PollMouseEvents(void) if (platform.touchSlot < MAX_TOUCH_POINTS) { platform.touchPosition[platform.touchSlot].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; - + // If this slot is active, it's a move. If not, we are just updating the buffer for when it becomes active. // Only set to MOVE if we haven't already detected a DOWN or UP event this frame if (platform.touchActive[platform.touchSlot] && touchAction == -1) touchAction = 2; // TOUCH_ACTION_MOVE @@ -2419,7 +2419,7 @@ static void PollMouseEvents(void) platform.touchActive[platform.touchSlot] = true; platform.touchId[platform.touchSlot] = event.value; // Use Tracking ID for unique IDs - + touchAction = 1; // TOUCH_ACTION_DOWN } else @@ -2429,7 +2429,7 @@ static void PollMouseEvents(void) platform.touchPosition[platform.touchSlot].x = -1; platform.touchPosition[platform.touchSlot].y = -1; platform.touchId[platform.touchSlot] = -1; - + // Force UP action if we haven't already set a DOWN action // (DOWN takes priority over UP if both happen in one frame, though rare) if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP @@ -2486,7 +2486,7 @@ static void PollMouseEvents(void) if (event.value > 0) { bool activateSlot0 = false; - + if (event.code == BTN_LEFT) activateSlot0 = true; // Mouse click always activates else if (event.code == BTN_TOUCH) { @@ -2534,11 +2534,11 @@ static void PollMouseEvents(void) if (!CORE.Input.Mouse.cursorLocked) { if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0; - if (CORE.Input.Mouse.currentPosition.x > CORE.Window.screen.width/CORE.Input.Mouse.scale.x) + if (CORE.Input.Mouse.currentPosition.x > CORE.Window.screen.width/CORE.Input.Mouse.scale.x) CORE.Input.Mouse.currentPosition.x = CORE.Window.screen.width/CORE.Input.Mouse.scale.x; if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0; - if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) + if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y; } @@ -2553,9 +2553,9 @@ static void PollMouseEvents(void) k++; } } - + CORE.Input.Touch.pointCount = k; - + // Clear remaining slots for (int i = k; i < MAX_TOUCH_POINTS; i++) { diff --git a/src/platforms/rcore_memory.c b/src/platforms/rcore_memory.c index 1b7a55fd8..fda3fe774 100644 --- a/src/platforms/rcore_memory.c +++ b/src/platforms/rcore_memory.c @@ -472,7 +472,7 @@ void PollInputEvents(void) } // TODO: Poll input events for current platform - + // Check for key pressed to exit if (kbhit()) { @@ -513,7 +513,7 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - + CORE.Window.ready = true; // TODO: Load OpenGL extensions diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index b52ca1bf0..244a53dd7 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -76,10 +76,10 @@ typedef struct { bool ourFullscreen; // Internal var to filter our handling of fullscreen vs the user handling of fullscreen int unmaximizedWidth; // Internal var to store the unmaximized window (canvas) width int unmaximizedHeight; // Internal var to store the unmaximized window (canvas) height - + char canvasId[64]; // Keep current canvas id where wasm app is running // NOTE: Useful when trying to run multiple wasms in different canvases in same webpage - + #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) unsigned int *pixels; // Pointer to pixel data buffer (RGBA 32bit format) #endif @@ -885,7 +885,7 @@ void SwapScreenBuffer(void) #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) // Update framebuffer rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, platform.pixels); - + // Copy framebuffer data into canvas EM_ASM({ const width = $0; @@ -1128,7 +1128,7 @@ void PollInputEvents(void) int InitPlatform(void) { SetCanvasIdJs(platform.canvasId, 64); // Get the current canvas id - + glfwSetErrorCallback(ErrorCallback); // Initialize GLFW internal global state @@ -1200,8 +1200,8 @@ int InitPlatform(void) glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) // Profiles Hint, only OpenGL 3.3 and above // Possible values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above! // glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context } @@ -1236,7 +1236,7 @@ int InitPlatform(void) // Init fullscreen toggle required var: platform.ourFullscreen = false; - + #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) // Avoid creating a WebGL canvas, avoid calling glfwCreateWindow() emscripten_set_canvas_element_size(platform.canvasId, CORE.Window.screen.width, CORE.Window.screen.height); @@ -1244,7 +1244,7 @@ int InitPlatform(void) const canvas = document.getElementById("canvas"); Module.canvas = canvas; }); - + // Load memory framebuffer with desired screen size // NOTE: Despite using a software framebuffer for blitting, GLFW still creates a WebGL canvas, // but it is not being used, on SwapScreenBuffer() the pure software renderer is used diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c index aeead6d9b..71dcdc2e7 100644 --- a/src/platforms/rcore_web_emscripten.c +++ b/src/platforms/rcore_web_emscripten.c @@ -636,25 +636,25 @@ void SetWindowSize(int width, int height) // - CSS canvas size: Web layout size, logical pixels // - Canvas contained framebuffer resolution // * Browser monitor, device pixel ratio (HighDPI) - - double canvasCssWidth = 0.0; + + double canvasCssWidth = 0.0; double canvasCssHeight = 0.0; emscripten_get_element_css_size(platform.canvasId, &canvasCssWidth, &canvasCssHeight); - + // NOTE: emscripten_get_canvas_element_size() returns canvas framebuffer size, not CSS canvas size - + // Get device pixel ratio // TODO: Should DPI be considered at this point? double dpr = emscripten_get_device_pixel_ratio(); // Set canvas framebuffer size emscripten_set_canvas_element_size(platform.canvasId, width*dpr, height*dpr); - + // Set canvas CSS size // TODO: Consider canvas CSS style if already scaled 100% EM_ASM({ Module.canvas.style.width = $0; }, width*dpr); EM_ASM({ Module.canvas.style.height = $0; }, height*dpr); - + SetupViewport(width*dpr, height*dpr); // Reset viewport and projection matrix for new size } @@ -704,7 +704,7 @@ Vector2 GetMonitorPosition(int monitor) // Get selected monitor width (currently used by monitor) int GetMonitorWidth(int monitor) { - // Get the width of the user's entire screen in CSS logical pixels, + // Get the width of the user's entire screen in CSS logical pixels, // no physical pixels, it would require multiplying by device pixel ratio // NOTE: Returned value is limited to the current monitor where the browser window is located int width = 0; @@ -715,7 +715,7 @@ int GetMonitorWidth(int monitor) // Get selected monitor height (currently used by monitor) int GetMonitorHeight(int monitor) { - // Get the height of the user's entire screen in CSS logical pixels, + // Get the height of the user's entire screen in CSS logical pixels, // no physical pixels, it would require multiplying by device pixel ratio // NOTE: Returned value is limited to the current monitor where the browser window is located int height = 0; @@ -865,7 +865,7 @@ void SwapScreenBuffer(void) #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) // Update framebuffer rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, platform.pixels); - + // Copy framebuffer data into canvas EM_ASM({ const width = $0; @@ -904,7 +904,7 @@ double GetTime(void) time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() */ time = emscripten_get_now()*1000.0; - + return time; } @@ -1137,7 +1137,7 @@ int InitPlatform(void) const canvas = document.getElementById(platform.canvasId); Module.canvas = canvas; }); - + // Load memory framebuffer with desired screen size platform.pixels = (unsigned int *)RL_CALLOC(CORE.Window.screen.width*CORE.Window.screen.height, sizeof(unsigned int)); } @@ -1145,7 +1145,7 @@ int InitPlatform(void) { attribs.majorVersion = 1; // WebGL 1.0 requested attribs.minorVersion = 0; - + // Create WebGL context platform.glContext = emscripten_webgl_create_context(platform.canvasId, &attribs); if (platform.glContext == 0) return 0; @@ -1156,7 +1156,7 @@ int InitPlatform(void) { attribs.majorVersion = 2; // WebGL 2.0 requested attribs.minorVersion = 0; - + // Create WebGL context platform.glContext = emscripten_webgl_create_context(platform.canvasId, &attribs); if (platform.glContext == 0) return 0; @@ -1216,7 +1216,7 @@ int InitPlatform(void) emscripten_set_keypress_callback(platform.canvasId, NULL, 1, EmscriptenKeyboardCallback); emscripten_set_keydown_callback(platform.canvasId, NULL, 1, EmscriptenKeyboardCallback); emscripten_set_keyup_callback(platform.canvasId, NULL, 1, EmscriptenKeyboardCallback); - + emscripten_set_click_callback(platform.canvasId, NULL, 1, EmscriptenMouseCallback); //emscripten_set_dblclick_callback(platform.canvasId, NULL, 1, EmscriptenMouseCallback); emscripten_set_mousedown_callback(platform.canvasId, NULL, 1, EmscriptenMouseCallback); @@ -1225,15 +1225,15 @@ int InitPlatform(void) emscripten_set_mousemove_callback(platform.canvasId, NULL, 1, EmscriptenMouseMoveCallback); emscripten_set_wheel_callback(platform.canvasId, NULL, 1, EmscriptenMouseWheelCallback); emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenPointerlockCallback); - + emscripten_set_touchstart_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback); emscripten_set_touchend_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback); emscripten_set_touchmove_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback); emscripten_set_touchcancel_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback); - + emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); - + // Trigger resize callback to force initial size EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); //---------------------------------------------------------------------------- @@ -1256,7 +1256,7 @@ int InitPlatform(void) // Close platform // NOTE: Platform closing is managed by browser, so, // this function is actually not required, but still -// implementing some logic behaviour +// implementing some logic behaviour void ClosePlatform(void) { if (platform.pixels != NULL) RL_FREE(platform.pixels); @@ -1319,14 +1319,14 @@ static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent * static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData) { EM_BOOL consumed = 1; - + switch (eventType) { case EMSCRIPTEN_EVENT_BLUR: FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break; // The canvas lost focus case EMSCRIPTEN_EVENT_FOCUS: FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break; default: consumed = 0; break; } - + return consumed; } @@ -1335,7 +1335,7 @@ static EM_BOOL EmscriptenVisibilityChangeCallback(int eventType, const Emscripte { if (visibilityChangeEvent->hidden) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN); // The window was hidden else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_HIDDEN); // The window was restored - + return 1; // The event was consumed by the callback handler } @@ -1405,7 +1405,7 @@ static EM_BOOL EmscriptenKeyboardCallback(int eventType, const EmscriptenKeyboar } break; default: break; } - + // TODO: Add char codes //unsigned int charCode // Check if there is space available in the queue for characters to be added @@ -1457,7 +1457,7 @@ static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent } break; default: break; } - + #if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) // Process mouse events as touches to be able to use mouse-gestures GestureEvent gestureEvent = { 0 }; @@ -1508,7 +1508,7 @@ static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseE double cssHeight = 0.0; emscripten_get_element_css_size(platform.canvasId, &cssWidth, &cssHeight); - int fbWidth = 0; + int fbWidth = 0; int fbHeight = 0; emscripten_get_canvas_element_size(platform.canvasId, &fbWidth, &fbHeight); @@ -1518,15 +1518,15 @@ static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseE int mouseX = (int)(mouseCssX*scaleX); int mouseY = (int)(mouseCssY*scaleY); - + CORE.Input.Mouse.currentPosition.x = mouseX;//(float)mouseEvent->canvasX; CORE.Input.Mouse.currentPosition.y = mouseY;//(float)mouseEvent->canvasY; - + // Shorter alternative: //double dpr = emscripten_get_device_pixel_ratio(); //int mouseX = (int)(e->canvasX*dpr); //int mouseY = (int)(e->canvasY*dpr); - + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; } @@ -1564,7 +1564,7 @@ static EM_BOOL EmscriptenMouseWheelCallback(int eventType, const EmscriptenWheel CORE.Input.Mouse.currentWheelMove.x = (float)wheelEvent->deltaX; CORE.Input.Mouse.currentWheelMove.y = (float)wheelEvent->deltaY; } - + return 1; // The event was consumed by the callback handler } diff --git a/src/raudio.c b/src/raudio.c index c65aaa134..e022447d7 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -2743,9 +2743,9 @@ static const char *GetFileExtension(const char *fileName) static const char *strprbrk(const char *text, const char *charset) { const char *latestMatch = NULL; - + for (; (text != NULL) && (text = strpbrk(text, charset)); latestMatch = text++) { } - + return latestMatch; } diff --git a/src/rcore.c b/src/rcore.c index af761cbb2..38bee25bf 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -320,7 +320,7 @@ typedef struct CoreData { char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state - // NOTE: Since key press logic involves comparing previous vs currrent key state, + // NOTE: Since key press logic involves comparing previous vs currrent key state, // key repeats needs to be handled specially char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame @@ -817,7 +817,7 @@ int GetScreenHeight(void) int GetRenderWidth(void) { int width = 0; - + if (CORE.Window.usingFbo) return CORE.Window.currentFbo.width; else width = CORE.Window.render.width; @@ -1735,7 +1735,7 @@ int GetRandomValue(int min, int max) { TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); } - + // NOTE: This one-line approach produces a non-uniform distribution, // as stated by Donald Knuth in the book The Art of Programming, so // using below approach for more uniform results @@ -2257,7 +2257,7 @@ const char *GetApplicationDirectory(void) #if defined(_WIN32) int len = 0; - + #if defined(UNICODE) unsigned short widePath[MAX_PATH]; len = GetModuleFileNameW(NULL, (wchar_t *)widePath, MAX_PATH); @@ -2265,7 +2265,7 @@ const char *GetApplicationDirectory(void) #else len = GetModuleFileNameA(NULL, appDir, MAX_PATH); #endif - + if (len > 0) { for (int i = len; i >= 0; --i) @@ -2282,7 +2282,7 @@ const char *GetApplicationDirectory(void) appDir[0] = '.'; appDir[1] = '\\'; } - + #elif defined(__linux__) unsigned int size = sizeof(appDir); @@ -2304,7 +2304,7 @@ const char *GetApplicationDirectory(void) appDir[0] = '.'; appDir[1] = '/'; } - + #elif defined(__APPLE__) uint32_t size = sizeof(appDir); @@ -2326,7 +2326,7 @@ const char *GetApplicationDirectory(void) appDir[0] = '.'; appDir[1] = '/'; } - + #elif defined(__FreeBSD__) size_t size = sizeof(appDir); @@ -2697,7 +2697,7 @@ unsigned char *DecodeDataBase64(const char *text, int *outputSize) ['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59, ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63 }; - + *outputSize = 0; if (text == NULL) return NULL; @@ -4241,7 +4241,7 @@ const char *TextFormat(const char *text, ...) char *currentBuffer = buffers[index]; memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using - + if (text != NULL) { va_list args; diff --git a/src/rlgl.h b/src/rlgl.h index cda64896c..b10942d88 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3391,7 +3391,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, // Activate trilinear filtering if mipmaps are available glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - + // Define the maximum number of mipmap levels to be used, 0 is base texture size glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipmapCount - 1); diff --git a/src/rmodels.c b/src/rmodels.c index 665b94147..da26a15f1 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -132,7 +132,7 @@ #ifndef MAX_MESH_VERTEX_BUFFERS #define MAX_MESH_VERTEX_BUFFERS 9 // Maximum vertex buffers (VBO) per mesh #endif -#ifndef MAX_FILEPATH_LENGTH +#ifndef MAX_FILEPATH_LENGTH #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) #endif @@ -4153,7 +4153,7 @@ RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform) // Test against all triangles in mesh for (int i = 0; i < triangleCount; i++) { - Vector3 a = { 0 }; + Vector3 a = { 0 }; Vector3 b = { 0 }; Vector3 c = { 0 }; Vector3 *vertdata = (Vector3 *)mesh.vertices; diff --git a/src/rtext.c b/src/rtext.c index b0f77a767..9c22b0993 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -700,9 +700,9 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz switch (type) { case FONT_DEFAULT: - case FONT_BITMAP: + case FONT_BITMAP: { - glyphs[k].image.data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, cp, + glyphs[k].image.data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, cp, &cpWidth, &cpHeight, &glyphs[k].offsetX, &glyphs[k].offsetY); } break; case FONT_SDF: @@ -1518,7 +1518,7 @@ const char *TextFormat(const char *text, ...) char *currentBuffer = buffers[index]; memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using - + if (text != NULL) { va_list args; @@ -1756,7 +1756,7 @@ char *TextReplace(const char *text, const char *search, const char *replacement) //tempLen -= lastReplacePos; //temp = strncpy(temp, replacement, tempLen - 1) + replaceLen; //tempLen -= replaceLen; - + text += lastReplacePos + searchLen; // Move to next "end of replace" } @@ -2059,7 +2059,7 @@ char *TextToCamel(const char *text) char *LoadUTF8(const int *codepoints, int length) { char *text = NULL; - + if ((codepoints != NULL) && (length > 0)) { // We allocate enough memory to fit all possible codepoints @@ -2096,7 +2096,7 @@ int *LoadCodepoints(const char *text, int *count) { int *codepoints = NULL; int codepointCount = 0; - + if (text != NULL) { int textLength = TextLength(text); @@ -2209,7 +2209,7 @@ int GetCodepoint(const char *text, int *codepointSize) 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ - + int codepoint = 0x3f; // Codepoint (defaults to '?') *codepointSize = 1; if (text == NULL) return codepoint; @@ -2504,7 +2504,7 @@ static Font LoadBMFont(const char *fileName) int charId = 0; int charX = 0; int charY = 0; - int charWidth = 0; + int charWidth = 0; int charHeight = 0; int charOffsetX = 0; int charOffsetY = 0; diff --git a/src/rtextures.c b/src/rtextures.c index 4208b40bd..37c04c439 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -1125,7 +1125,7 @@ Image GenImageCellular(int width, int height, int tileSize) Image GenImageText(int width, int height, const char *text) { Image image = { 0 }; - + int imageSize = width*height; image.width = width; image.height = height; @@ -1487,7 +1487,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co Image imText = { 0 }; #if defined(SUPPORT_MODULE_RTEXT) if (text == NULL) return imText; - + int textLength = (int)strlen(text); // Get length of text in bytes int textOffsetX = 0; // Image drawing position X int textOffsetY = 0; // Offset between lines (on linebreak '\n') From 5a3391fdce046bc5473e52afbd835dd2dc127146 Mon Sep 17 00:00:00 2001 From: GlitchLens <46534888+oneafter@users.noreply.github.com> Date: Thu, 1 Jan 2026 23:35:12 +0800 Subject: [PATCH 064/117] [rtext] Fix multiple security vulnerabilities in font loading (#5433, #5434, #5436) (#5450) --- src/rtext.c | 51 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 9c22b0993..9801bf698 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -743,8 +743,14 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz stbtt_GetCodepointHMetrics(&fontInfo, cp, &glyphs[k].advanceX, NULL); glyphs[k].advanceX = (int)((float)glyphs[k].advanceX*scaleFactor); + // [Security Fix] Prevent integer overflow/negative allocation + // Issue #5436: Malicious font files may contain negative advanceX, + // causing calloc overflow or crash + if (glyphs[k].advanceX < 0) glyphs[k].advanceX = 0; + Image imSpace = { - .data = RL_CALLOC(glyphs[k].advanceX*fontSize, 2), + // Only allocate memory if width > 0, otherwise set to NULL + .data = (glyphs[k].advanceX > 0) ? RL_CALLOC(glyphs[k].advanceX*fontSize, 2) : NULL, .width = glyphs[k].advanceX, .height = fontSize, .mipmaps = 1, @@ -853,7 +859,8 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp } #endif - atlas.data = (unsigned char *)RL_CALLOC(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp) + int atlasDataSize = atlas.width * atlas.height; // Save total size for bounds checking + atlas.data = (unsigned char *)RL_CALLOC(1, atlasDataSize); // Create a bitmap to store characters (8 bpp) atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; atlas.mipmaps = 1; @@ -898,7 +905,17 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp { for (int x = 0; x < glyphs[i].image.width; x++) { - ((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)glyphs[i].image.data)[y*glyphs[i].image.width + x]; + int destX = offsetX + x; + int destY = offsetY + y; + + // Security fix: check both lower and upper bounds + // destX >= 0: prevent heap underflow (#5434) + // destX < atlas.width: prevent heap overflow (#5433) + if (destX >= 0 && destX < atlas.width && destY >= 0 && destY < atlas.height) + { + ((unsigned char *)atlas.data)[destY * atlas.width + destX] = + ((unsigned char *)glyphs[i].image.data)[y * glyphs[i].image.width + x]; + } } } @@ -946,7 +963,15 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp { for (int x = 0; x < glyphs[i].image.width; x++) { - ((unsigned char *)atlas.data)[(rects[i].y + padding + y)*atlas.width + (rects[i].x + padding + x)] = ((unsigned char *)glyphs[i].image.data)[y*glyphs[i].image.width + x]; + int destX = rects[i].x + padding + x; + int destY = rects[i].y + padding + y; + + // Security fix: check both lower and upper bounds + if (destX >= 0 && destX < atlas.width && destY >= 0 && destY < atlas.height) + { + ((unsigned char *)atlas.data)[destY * atlas.width + destX] = + ((unsigned char *)glyphs[i].image.data)[y * glyphs[i].image.width + x]; + } } } } @@ -960,14 +985,18 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp #if defined(SUPPORT_FONT_ATLAS_WHITE_REC) // Add a 3x3 white rectangle at the bottom-right corner of the generated atlas, - // useful to use as the white texture to draw shapes with raylib, using this rectangle - // shapes and text can be backed into a single draw call: SetShapesTexture() - for (int i = 0, k = atlas.width*atlas.height - 1; i < 3; i++) + // useful to use as the white texture to draw shapes with raylib. + // [Security Fix] Ensure the atlas is large enough to hold a 3x3 rectangle. + // This prevents heap underflow when width < 3 or height < 3 (Fixes #5434 variant) + if (atlas.width >= 3 && atlas.height >= 3) { - ((unsigned char *)atlas.data)[k - 0] = 255; - ((unsigned char *)atlas.data)[k - 1] = 255; - ((unsigned char *)atlas.data)[k - 2] = 255; - k -= atlas.width; + for (int i = 0, k = atlas.width*atlas.height - 1; i < 3; i++) + { + ((unsigned char *)atlas.data)[k - 0] = 255; + ((unsigned char *)atlas.data)[k - 1] = 255; + ((unsigned char *)atlas.data)[k - 2] = 255; + k -= atlas.width; + } } #endif From c07d075a63ec8899884841365265040ff960f9b3 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 1 Jan 2026 16:54:44 +0100 Subject: [PATCH 065/117] REVIEWED: Security checks formatting and comments --- src/rtext.c | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 9801bf698..9359e3ea3 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -742,21 +742,19 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz { stbtt_GetCodepointHMetrics(&fontInfo, cp, &glyphs[k].advanceX, NULL); glyphs[k].advanceX = (int)((float)glyphs[k].advanceX*scaleFactor); - - // [Security Fix] Prevent integer overflow/negative allocation - // Issue #5436: Malicious font files may contain negative advanceX, - // causing calloc overflow or crash - if (glyphs[k].advanceX < 0) glyphs[k].advanceX = 0; - + Image imSpace = { - // Only allocate memory if width > 0, otherwise set to NULL - .data = (glyphs[k].advanceX > 0) ? RL_CALLOC(glyphs[k].advanceX*fontSize, 2) : NULL, + .data = NULL, .width = glyphs[k].advanceX, .height = fontSize, .mipmaps = 1, .format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE }; + // Only allocate space image if required + if (glyphs[k].advanceX > 0) imSpace.data = RL_CALLOC(glyphs[k].advanceX*fontSize, 1); + else glyphs[k].advanceX = 0; + glyphs[k].image = imSpace; } @@ -859,8 +857,8 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp } #endif - int atlasDataSize = atlas.width * atlas.height; // Save total size for bounds checking - atlas.data = (unsigned char *)RL_CALLOC(1, atlasDataSize); // Create a bitmap to store characters (8 bpp) + int atlasDataSize = atlas.width*atlas.height; // Save total size for bounds checking + atlas.data = (unsigned char *)RL_CALLOC(atlasDataSize, 1); // Create a bitmap to store characters (8 bpp) atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; atlas.mipmaps = 1; @@ -908,13 +906,11 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp int destX = offsetX + x; int destY = offsetY + y; - // Security fix: check both lower and upper bounds - // destX >= 0: prevent heap underflow (#5434) - // destX < atlas.width: prevent heap overflow (#5433) - if (destX >= 0 && destX < atlas.width && destY >= 0 && destY < atlas.height) + // Security: check both lower and upper bounds + if ((destX >= 0) && (destX < atlas.width) && (destY >= 0) && (destY < atlas.height)) { - ((unsigned char *)atlas.data)[destY * atlas.width + destX] = - ((unsigned char *)glyphs[i].image.data)[y * glyphs[i].image.width + x]; + ((unsigned char *)atlas.data)[destY*atlas.width + destX] = + ((unsigned char *)glyphs[i].image.data)[y*glyphs[i].image.width + x]; } } } @@ -985,10 +981,9 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp #if defined(SUPPORT_FONT_ATLAS_WHITE_REC) // Add a 3x3 white rectangle at the bottom-right corner of the generated atlas, - // useful to use as the white texture to draw shapes with raylib. - // [Security Fix] Ensure the atlas is large enough to hold a 3x3 rectangle. - // This prevents heap underflow when width < 3 or height < 3 (Fixes #5434 variant) - if (atlas.width >= 3 && atlas.height >= 3) + // useful to use as the white texture to draw shapes with raylib + // Security: ensure the atlas is large enough to hold a 3x3 rectangle + if ((atlas.width >= 3) && (atlas.height >= 3)) { for (int i = 0, k = atlas.width*atlas.height - 1; i < 3; i++) { From c9a456e273e9fb0581ebce4046ada7afb26a679e Mon Sep 17 00:00:00 2001 From: Jeremiah Donley <108106416+JJLDonley@users.noreply.github.com> Date: Fri, 2 Jan 2026 07:14:25 -0500 Subject: [PATCH 066/117] Add DenoRaylib550 binding to BINDINGS.md (#5462) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 37274b3be..6ef57035f 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -29,6 +29,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers | [bindbc-raylib3](https://github.com/o3o/bindbc-raylib3) | **5.0** | [D](https://dlang.org) | BSL-1.0 | | [dray](https://github.com/redthing1/dray) | **5.0** | [D](https://dlang.org) | Apache-2.0 | | [raylib-d](https://github.com/schveiguy/raylib-d) | **5.5** | [D](https://dlang.org) | Zlib | +| [DenoRaylib550](https://github.com/JJLDonley/DenoRaylib550) | **5.5** | [Deno](https://deno.land) | MIT | | [rayex](https://github.com/shiryel/rayex) | 3.7 | [elixir](https://elixir-lang.org) | Apache-2.0 | | [raylib-elle](https://github.com/acquitelol/elle/blob/rewrite/std/raylib.le) | **5.5** | [Elle](https://github.com/acquitelol/elle) | GPL-3.0 | | [raylib-factor](https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor) | 4.5 | [Factor](https://factorcode.org) | BSD | From 980e4d0ad3ea5fa32bb53c0de770b3ae3e4db2e2 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Fri, 2 Jan 2026 04:15:25 -0800 Subject: [PATCH 067/117] Use the size of the texture as the V scale so repeatable textures work well (#5463) --- examples/textures/textures_textured_curve.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/textures/textures_textured_curve.c b/examples/textures/textures_textured_curve.c index abf78c88a..f8d207d83 100644 --- a/examples/textures/textures_textured_curve.c +++ b/examples/textures/textures_textured_curve.c @@ -190,7 +190,7 @@ static void DrawTexturedCurve(void) Vector2 normal = Vector2Normalize((Vector2){ -delta.y, delta.x }); // The v texture coordinate of the segment (add up the length of all the segments so far) - float v = previousV + Vector2Length(delta); + float v = previousV + Vector2Length(delta) / (float)(texRoad.height * 2); // Make sure the start point has a normal if (!tangentSet) From 416af51a93772d1736b6dedc0d273a8079cf3381 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 2 Jan 2026 13:40:15 +0100 Subject: [PATCH 068/117] Update year to 2026 --- LICENSE | 2 +- examples/Makefile | 8 +++++--- examples/Makefile.Web | 4 ++-- projects/VSCode/main.c | 2 +- src/Makefile | 2 +- src/config.h | 2 +- src/external/rl_gputex.h | 2 +- src/platforms/rcore_android.c | 2 +- src/platforms/rcore_desktop_glfw.c | 2 +- src/platforms/rcore_desktop_rgfw.c | 2 +- src/platforms/rcore_desktop_sdl.c | 2 +- src/platforms/rcore_desktop_win32.c | 2 +- src/platforms/rcore_drm.c | 2 +- src/platforms/rcore_template.c | 2 +- src/platforms/rcore_web.c | 2 +- src/raudio.c | 4 ++-- src/raylib.h | 2 +- src/raymath.h | 2 +- src/rcamera.h | 2 +- src/rcore.c | 4 ++-- src/rgestures.h | 2 +- src/rglfw.c | 2 +- src/rlgl.h | 2 +- src/rmodels.c | 4 ++-- src/rshapes.c | 2 +- src/rtext.c | 4 ++-- src/rtextures.c | 4 ++-- src/utils.c | 4 ++-- src/utils.h | 2 +- tools/rexm/README.md | 2 +- tools/rexm/rexm.c | 2 +- tools/rlparser/LICENSE | 2 +- tools/rlparser/README.md | 2 +- tools/rlparser/rlparser.c | 4 ++-- 34 files changed, 46 insertions(+), 44 deletions(-) diff --git a/LICENSE b/LICENSE index e96f876a2..bc6f4b851 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) +Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) This software is provided "as-is", without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. diff --git a/examples/Makefile b/examples/Makefile index 9983d8705..b85a448c8 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -30,7 +30,7 @@ # > PLATFORM_ANDROID: # - Android (ARM, ARM64) # -# Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. @@ -205,10 +205,12 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) endif endif ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) - MAKE = mingw32-make + ifeq ($(PLATFORM_OS),WINDOWS) + MAKE = mingw32-make + endif endif ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) - ifeq ($(OS),Windows_NT) + ifeq ($(PLATFORM_OS),WINDOWS) MAKE = mingw32-make else EMMAKE != type emmake diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 5718088ac..56d5bd83c 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -30,7 +30,7 @@ # > PLATFORM_ANDROID: # - Android (ARM, ARM64) # -# Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. @@ -208,7 +208,7 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) MAKE = mingw32-make endif ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW)) - ifeq ($(OS),Windows_NT) + ifeq ($(PLATFORM_OS),WINDOWS) MAKE = mingw32-make else EMMAKE != type emmake diff --git a/projects/VSCode/main.c b/projects/VSCode/main.c index ea394de58..7a5d89000 100644 --- a/projects/VSCode/main.c +++ b/projects/VSCode/main.c @@ -15,7 +15,7 @@ * This example has been created using raylib 1.0 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) * ********************************************************************************************/ diff --git a/src/Makefile b/src/Makefile index bc84abece..89dd759aa 100644 --- a/src/Makefile +++ b/src/Makefile @@ -33,7 +33,7 @@ # Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. # Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. # -# Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) +# Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) # # This software is provided "as-is", without any express or implied warranty. In no event # will the authors be held liable for any damages arising from the use of this software. diff --git a/src/config.h b/src/config.h index 9f54edd23..9a1d22de3 100644 --- a/src/config.h +++ b/src/config.h @@ -6,7 +6,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2018-2025 Ahmad Fatoum & Ramon Santamaria (@raysan5) +* Copyright (c) 2018-2026 Ahmad Fatoum and Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index 9c1092695..033045bc8 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -62,7 +62,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 4f36f0a5b..6d3d68f24 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -27,7 +27,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) and contributors +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 24b6d8d18..20b7449b8 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -30,7 +30,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) and contributors +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index b906fd103..05960c14b 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -29,7 +29,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5), Colleague Riley and contributors +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5), Colleague Riley and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index cc3f7c6ae..0279c0c28 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -29,7 +29,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) and contributors +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 8d9bc6c8b..7ef01a1a0 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -26,7 +26,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) and contributors +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 8db332b06..eedb915e2 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -29,7 +29,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) and contributors +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index b22d3f2f5..87cd2e21e 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -27,7 +27,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) and contributors +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 244a53dd7..0056849dd 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -26,7 +26,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) and contributors +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/raudio.c b/src/raudio.c index e022447d7..98326727f 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -50,7 +50,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -1134,7 +1134,7 @@ bool ExportWaveAsCode(Wave wave, const char *fileName) byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n"); byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); - byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2025 Ramon Santamaria (@raysan5) //\n"); + byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2026 Ramon Santamaria (@raysan5) //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "//////////////////////////////////////////////////////////////////////////////////\n\n"); diff --git a/src/raylib.h b/src/raylib.h index ba80e40c7..2d411f896 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -62,7 +62,7 @@ * raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software: * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/raymath.h b/src/raymath.h index 8d5b1b2a9..6ab5e2b4c 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -37,7 +37,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2015-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/rcamera.h b/src/rcamera.h index 72552ec15..82f14fecd 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -20,7 +20,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2022-2025 Christoph Wagner (@Crydsch) & Ramon Santamaria (@raysan5) +* Copyright (c) 2022-2026 Christoph Wagner (@Crydsch) and Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/rcore.c b/src/rcore.c index 38bee25bf..e16c6412a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -70,7 +70,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) and contributors +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -3253,7 +3253,7 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName) byteCount += sprintf(txtData + byteCount, "# more info and bugs-report: github.com/raysan5/raylib\n"); byteCount += sprintf(txtData + byteCount, "# feedback and support: ray[at]raylib.com\n"); byteCount += sprintf(txtData + byteCount, "#\n"); - byteCount += sprintf(txtData + byteCount, "# Copyright (c) 2023-2025 Ramon Santamaria (@raysan5)\n"); + byteCount += sprintf(txtData + byteCount, "# Copyright (c) 2023-2026 Ramon Santamaria (@raysan5)\n"); byteCount += sprintf(txtData + byteCount, "#\n\n"); // Add events data diff --git a/src/rgestures.h b/src/rgestures.h index f601a4790..e6cb86300 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -21,7 +21,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/rglfw.c b/src/rglfw.c index b167955bc..53399aa13 100644 --- a/src/rglfw.c +++ b/src/rglfw.c @@ -7,7 +7,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2017-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2017-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/rlgl.h b/src/rlgl.h index b10942d88..ab85569bb 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -89,7 +89,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/rmodels.c b/src/rmodels.c index da26a15f1..3ee429900 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -21,7 +21,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -1987,7 +1987,7 @@ bool ExportMesh(Mesh mesh, const char *fileName) byteCount += sprintf(txtData + byteCount, "# // more info and bugs-report: github.com/raysan5/raylib //\n"); byteCount += sprintf(txtData + byteCount, "# // feedback and support: ray[at]raylib.com //\n"); byteCount += sprintf(txtData + byteCount, "# // //\n"); - byteCount += sprintf(txtData + byteCount, "# // Copyright (c) 2018-2025 Ramon Santamaria (@raysan5) //\n"); + byteCount += sprintf(txtData + byteCount, "# // Copyright (c) 2018-2026 Ramon Santamaria (@raysan5) //\n"); byteCount += sprintf(txtData + byteCount, "# // //\n"); byteCount += sprintf(txtData + byteCount, "# //////////////////////////////////////////////////////////////////////////////////\n\n"); byteCount += sprintf(txtData + byteCount, "# Vertex Count: %i\n", mesh.vertexCount); diff --git a/src/rshapes.c b/src/rshapes.c index 528a362d5..e828b98bc 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -25,7 +25,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/rtext.c b/src/rtext.c index 9359e3ea3..1fd9a306d 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -34,7 +34,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -1066,7 +1066,7 @@ bool ExportFontAsCode(Font font, const char *fileName) byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n"); byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); - byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2025 Ramon Santamaria (@raysan5) //\n"); + byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2026 Ramon Santamaria (@raysan5) //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "// ---------------------------------------------------------------------------------- //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); diff --git a/src/rtextures.c b/src/rtextures.c index 37c04c439..59000940e 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -42,7 +42,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -765,7 +765,7 @@ bool ExportImageAsCode(Image image, const char *fileName) byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n"); byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); - byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2025 Ramon Santamaria (@raysan5) //\n"); + byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2026 Ramon Santamaria (@raysan5) //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n"); diff --git a/src/utils.c b/src/utils.c index 09158893a..82d7d0aa2 100644 --- a/src/utils.c +++ b/src/utils.c @@ -10,7 +10,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -307,7 +307,7 @@ bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileN byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n"); byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); - byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2022-2025 Ramon Santamaria (@raysan5) //\n"); + byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2022-2026 Ramon Santamaria (@raysan5) //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n"); diff --git a/src/utils.h b/src/utils.h index 271d0d2c7..7d79c2188 100644 --- a/src/utils.h +++ b/src/utils.h @@ -5,7 +5,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/tools/rexm/README.md b/tools/rexm/README.md index 232ef51d6..14704b5ea 100644 --- a/tools/rexm/README.md +++ b/tools/rexm/README.md @@ -102,4 +102,4 @@ char *TextReplaceBetween(const char *text, const char *begin, const char *end, c `rexm` is an **open source** project, licensed under an unmodified [zlib/libpng license](LICENSE) -*Copyright (c) 2025 Ramon Santamaria ([@raysan5](https://github.com/raysan5))* +*Copyright (c) 2025-2026 Ramon Santamaria ([@raysan5](https://github.com/raysan5))* diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 36152e560..41aa7a85c 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -2840,7 +2840,7 @@ static void UpdateSourceMetadata(const char *exSrcPath, const rlExampleInfo *inf if (exTextUpdated[2] != NULL) exTextUpdatedPtr = exTextUpdated[2]; // Update copyright message - // String: "* Copyright (c) 2019-2025 Contributor Name (@github_user) and Ramon Santamaria (@raysan5)" + // String: "* Copyright (c) 2019-2026 Contributor Name (@github_user) and Ramon Santamaria (@raysan5)" if (info->yearCreated == info->yearReviewed) { exTextUpdated[3] = TextReplaceBetween(exTextUpdatedPtr, "Copyright (c) ", ")", diff --git a/tools/rlparser/LICENSE b/tools/rlparser/LICENSE index 7ed4b8722..4ce3bbb8d 100644 --- a/tools/rlparser/LICENSE +++ b/tools/rlparser/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2025 Ramon Santamaria (@raysan5) +Copyright (c) 2021-2026 Ramon Santamaria (@raysan5) This software is provided "as-is", without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. diff --git a/tools/rlparser/README.md b/tools/rlparser/README.md index 0e4f9b739..cf009a891 100644 --- a/tools/rlparser/README.md +++ b/tools/rlparser/README.md @@ -19,7 +19,7 @@ Check `rlparser.c` for details about those structs. // // // more info and bugs-report: github.com/raysan5/raylib/tools/rlparser // // // -// Copyright (c) 2021-2025 Ramon Santamaria (@raysan5) // +// Copyright (c) 2021-2026 Ramon Santamaria (@raysan5) // // // ////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/rlparser/rlparser.c b/tools/rlparser/rlparser.c index d5b03fa01..c291c3038 100644 --- a/tools/rlparser/rlparser.c +++ b/tools/rlparser/rlparser.c @@ -52,7 +52,7 @@ raylib-parser is licensed under an unmodified zlib/libpng license, which is an OSI-certified, BSD-like license that allows static linking with closed source software: - Copyright (c) 2021-2025 Ramon Santamaria (@raysan5) + Copyright (c) 2021-2026 Ramon Santamaria (@raysan5) **********************************************************************************************/ @@ -1084,7 +1084,7 @@ static void ShowCommandLineInfo(void) printf("// //\n"); printf("// more info and bugs-report: github.com/raysan5/raylib/tools/rlparser //\n"); printf("// //\n"); - printf("// Copyright (c) 2021-2025 Ramon Santamaria (@raysan5) //\n"); + printf("// Copyright (c) 2021-2026 Ramon Santamaria (@raysan5) //\n"); printf("// //\n"); printf("//////////////////////////////////////////////////////////////////////////////////\n\n"); From ca89934ed5af9161f781a9b35b808b765dc40f3a Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 2 Jan 2026 13:53:20 +0100 Subject: [PATCH 069/117] Update year to 2026 --- src/platforms/rcore_memory.c | 2 +- src/platforms/rcore_web_emscripten.c | 2 +- tools/rexm/rexm.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/platforms/rcore_memory.c b/src/platforms/rcore_memory.c index fda3fe774..04d343164 100644 --- a/src/platforms/rcore_memory.c +++ b/src/platforms/rcore_memory.c @@ -27,7 +27,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2025 Ramon Santamaria (@raysan5) and contributors +* Copyright (c) 2025-2026 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c index 71dcdc2e7..28d530e97 100644 --- a/src/platforms/rcore_web_emscripten.c +++ b/src/platforms/rcore_web_emscripten.c @@ -25,7 +25,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2025 Ramon Santamaria (@raysan5) and contributors +* Copyright (c) 2025-2026 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 41aa7a85c..d3ff18289 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -30,7 +30,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2025 Ramon Santamaria (@raysan5) +* Copyright (c) 2025-2026 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -1881,7 +1881,7 @@ int main(int argc, char *argv[]) printf("// rexm [raylib examples manager] - A simple command-line tool to manage raylib examples //\n"); printf("// powered by raylib v5.6-dev //\n"); printf("// //\n"); - printf("// Copyright (c) 2025 Ramon Santamaria (@raysan5) //\n"); + printf("// Copyright (c) 2025-2026 Ramon Santamaria (@raysan5) //\n"); printf("// //\n"); printf("////////////////////////////////////////////////////////////////////////////////////////////\n\n"); From 942f93db55105495fd39acfc0666997b4efb4c98 Mon Sep 17 00:00:00 2001 From: sleeptightAnsiC <91839286+sleeptightAnsiC@users.noreply.github.com> Date: Fri, 2 Jan 2026 18:36:22 +0100 Subject: [PATCH 070/117] fix(build): do not use != assignment in Makefiles (#5464) GNU Make 3.81 that ships with MacOSX does not understand '!= ...' assignment so we use ':= $(shell ...)' instead which have the same behavior here. Additionally, I have changed the use of 'type' to 'command -v' because assigning the result of 'type' to variable named 'EMMAKE' does not make much sense. I also reused this variable. For more detailed information read the linked issue. Fixes: https://github.com/raysan5/raylib/issues/5460 --- examples/Makefile | 4 ++-- examples/Makefile.Web | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index b85a448c8..3cbc2ffc1 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -213,9 +213,9 @@ ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_R ifeq ($(PLATFORM_OS),WINDOWS) MAKE = mingw32-make else - EMMAKE != type emmake + EMMAKE := $(shell command -v emmake) ifneq (, $(EMMAKE)) - MAKE = emmake make + MAKE = $(EMMAKE) make else MAKE = mingw32-make endif diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 56d5bd83c..fe4de1330 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -211,9 +211,9 @@ ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_R ifeq ($(PLATFORM_OS),WINDOWS) MAKE = mingw32-make else - EMMAKE != type emmake + EMMAKE := $(shell command -v emmake) ifneq (, $(EMMAKE)) - MAKE = emmake make + MAKE = $(EMMAKE) make else MAKE = mingw32-make endif From c92de5f108850b91f140a932ca25aa3508336d58 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 2 Jan 2026 18:43:28 +0100 Subject: [PATCH 071/117] REVIEWED: Comments about intrinsics support #5316 --- src/raymath.h | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/raymath.h b/src/raymath.h index 6ab5e2b4c..57e3dac51 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -177,26 +177,24 @@ typedef struct float16 { #if defined(RAYMATH_USE_SIMD_INTRINSICS) // SIMD is used on the most costly raymath function MatrixMultiply() // NOTE: Only SSE intrinsics support implemented - // TODO: Consider support for other SIMD instrinsics + // TODO: Consider support for other SIMD instrinsics: + // - SSEx, AVX, AVX2, FMA, NEON, RVV /* #if defined(__SSE4_2__) - #define SW_HAS_SSE42 #include + #define RAYMATH_SSE42_ENABLED #elif defined(__SSE4_1__) - #define SW_HAS_SSE41 #include + #define RAYMATH_SSE41_ENABLED #elif defined(__SSSE3__) - #define SW_HAS_SSSE3 #include + #define RAYMATH_SSSE3_ENABLED #elif defined(__SSE3__) - #define SW_HAS_SSE3 #include + #define RAYMATH_SSE3_ENABLED #elif defined(__SSE2__) || (defined(_M_AMD64) || defined(_M_X64)) // SSE2 x64 - #define SW_HAS_SSE2 #include - #elif defined(__SSE__) - #define SW_HAS_SSE - #include + #define RAYMATH_SSE2_ENABLED #endif */ #if defined(__SSE__) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP >= 1)) From f67e70bb4763635740cf5a3f7e334f7e0a1f2854 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Fri, 2 Jan 2026 23:59:34 -0800 Subject: [PATCH 072/117] Fix typecast warnings in rcore (#5466) --- src/platforms/rcore_desktop_glfw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 20b7449b8..84b021573 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -227,8 +227,8 @@ void ToggleFullscreen(void) if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) { Vector2 scaleDpi = GetWindowScaleDPI(); - CORE.Window.screen.width *= scaleDpi.x; - CORE.Window.screen.height *= scaleDpi.y; + CORE.Window.screen.width = (unsigned int)(CORE.Window.screen.width * scaleDpi.x); + CORE.Window.screen.height = (unsigned int)(CORE.Window.screen.height * scaleDpi.y); } #endif @@ -306,8 +306,8 @@ void ToggleBorderlessWindowed(void) if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) { Vector2 scaleDpi = GetWindowScaleDPI(); - CORE.Window.screen.width *= scaleDpi.x; - CORE.Window.screen.height *= scaleDpi.y; + CORE.Window.screen.width = (unsigned int)(CORE.Window.screen.width * scaleDpi.x); + CORE.Window.screen.height = (unsigned int)(CORE.Window.screen.height * scaleDpi.y); } #endif From b00cbdaf49a7140db890d4c5621c422c83872eab Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Sat, 3 Jan 2026 13:38:51 -0800 Subject: [PATCH 073/117] Cleanup warnings in examples (#5467) --- examples/audio/audio_music_stream.c | 4 +- examples/core/core_input_gamepad.c | 4 +- examples/core/core_viewport_scaling.c | 12 +++--- examples/models/models_decals.c | 39 ++++++++++--------- examples/shapes/shapes_ball_physics.c | 16 ++++---- examples/shapes/shapes_kaleidoscope.c | 6 +-- examples/shapes/shapes_penrose_tile.c | 10 ++--- examples/text/text_inline_styling.c | 6 +-- examples/text/text_strings_management.c | 16 ++++---- examples/textures/textures_screen_buffer.c | 2 +- .../examples/shapes_hilbert_curve.vcxproj | 16 ++++---- .../examples/shapes_penrose_tile.vcxproj | 16 ++++---- .../examples/shapes_rlgl_color_wheel.vcxproj | 16 ++++---- 13 files changed, 83 insertions(+), 80 deletions(-) diff --git a/examples/audio/audio_music_stream.c b/examples/audio/audio_music_stream.c index 05ec1c2d6..6e1dfc8c5 100644 --- a/examples/audio/audio_music_stream.c +++ b/examples/audio/audio_music_stream.c @@ -113,7 +113,7 @@ int main(void) DrawText("LEFT-RIGHT for PAN CONTROL", 320, 74, 10, DARKBLUE); DrawRectangle(300, 100, 200, 12, LIGHTGRAY); DrawRectangleLines(300, 100, 200, 12, GRAY); - DrawRectangle(300 + (pan + 1.0)/2.0f*200 - 5, 92, 10, 28, DARKGRAY); + DrawRectangle((int)(300 + (pan + 1.0f)/2.0f*200 - 5), 92, 10, 28, DARKGRAY); DrawRectangle(200, 200, 400, 12, LIGHTGRAY); DrawRectangle(200, 200, (int)(timePlayed*400.0f), 12, MAROON); @@ -125,7 +125,7 @@ int main(void) DrawText("UP-DOWN for VOLUME CONTROL", 320, 334, 10, DARKGREEN); DrawRectangle(300, 360, 200, 12, LIGHTGRAY); DrawRectangleLines(300, 360, 200, 12, GRAY); - DrawRectangle(300 + volume*200 - 5, 352, 10, 28, DARKGRAY); + DrawRectangle((int)(300 + volume*200 - 5), 352, 10, 28, DARKGRAY); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/core/core_input_gamepad.c b/examples/core/core_input_gamepad.c index 3c9454318..a9e0660e0 100644 --- a/examples/core/core_input_gamepad.c +++ b/examples/core/core_input_gamepad.c @@ -67,7 +67,7 @@ int main(void) if (IsKeyPressed(KEY_RIGHT)) gamepad++; Vector2 mousePosition = GetMousePosition(); - vibrateButton = (Rectangle){ 10, 70 + 20*GetGamepadAxisCount(gamepad) + 20, 75, 24 }; + vibrateButton = (Rectangle){ 10, 70.0f + 20*GetGamepadAxisCount(gamepad) + 20, 75, 24 }; if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && CheckCollisionPointRec(mousePosition, vibrateButton)) SetGamepadVibration(gamepad, 1.0, 1.0, 1.0); //---------------------------------------------------------------------------------- @@ -262,7 +262,7 @@ int main(void) // Draw vibrate button DrawRectangleRec(vibrateButton, SKYBLUE); - DrawText("VIBRATE", vibrateButton.x + 14, vibrateButton.y + 1, 10, DARKGRAY); + DrawText("VIBRATE", (int)(vibrateButton.x + 14), (int)(vibrateButton.y + 1), 10, DARKGRAY); if (GetGamepadButtonPressed() != GAMEPAD_BUTTON_UNKNOWN) DrawText(TextFormat("DETECTED BUTTON: %i", GetGamepadButtonPressed()), 10, 430, 10, RED); else DrawText("DETECTED BUTTON: NONE", 10, 430, 10, GRAY); diff --git a/examples/core/core_viewport_scaling.c b/examples/core/core_viewport_scaling.c index adcd51ea3..6ff5ac9c4 100644 --- a/examples/core/core_viewport_scaling.c +++ b/examples/core/core_viewport_scaling.c @@ -112,16 +112,16 @@ int main(void) if (CheckCollisionPointRec(mousePosition, decreaseResolutionButton) && mousePressed) { resolutionIndex = (resolutionIndex + RESOLUTION_COUNT - 1)%RESOLUTION_COUNT; - gameWidth = resolutionList[resolutionIndex].x; - gameHeight = resolutionList[resolutionIndex].y; + gameWidth = (int)resolutionList[resolutionIndex].x; + gameHeight = (int)resolutionList[resolutionIndex].y; ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target); } if (CheckCollisionPointRec(mousePosition, increaseResolutionButton) && mousePressed) { resolutionIndex = (resolutionIndex + 1)%RESOLUTION_COUNT; - gameWidth = resolutionList[resolutionIndex].x; - gameHeight = resolutionList[resolutionIndex].y; + gameWidth = (int)resolutionList[resolutionIndex].x; + gameHeight = (int)resolutionList[resolutionIndex].y; ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target); } @@ -145,7 +145,7 @@ int main(void) // Draw our scene to the render texture BeginTextureMode(target); ClearBackground(WHITE); - DrawCircle(textureMousePosition.x, textureMousePosition.y, 20.0f, LIME); + DrawCircleV(textureMousePosition, 20.0f, LIME); EndTextureMode(); // Draw render texture to main framebuffer @@ -159,7 +159,7 @@ int main(void) // Draw info box Rectangle infoRect = (Rectangle){5, 5, 330, 105}; DrawRectangleRec(infoRect, Fade(LIGHTGRAY, 0.7f)); - DrawRectangleLines(infoRect.x, infoRect.y, infoRect.width, infoRect.height, BLUE); + DrawRectangleLinesEx(infoRect, 1, BLUE); DrawText(TextFormat("Window Resolution: %d x %d", screenWidth, screenHeight), 15, 15, 10, BLACK); DrawText(TextFormat("Game Resolution: %d x %d", gameWidth, gameHeight), 15, 30, 10, BLACK); diff --git a/examples/models/models_decals.c b/examples/models/models_decals.c index f556139e1..f35794daa 100644 --- a/examples/models/models_decals.c +++ b/examples/models/models_decals.c @@ -45,7 +45,10 @@ static void FreeMeshBuilder(MeshBuilder *mb); static Mesh BuildMesh(MeshBuilder *mb); static Mesh GenMeshDecal(Model inputModel, Matrix projection, float decalSize, float decalOffset); static Vector3 ClipSegment(Vector3 v0, Vector3 v1, Vector3 p, float s); -#define FreeDecalMeshData() GenMeshDecal((Model){ .meshCount = -1.0f }, (Matrix){ 0 }, 0.0f, 0.0f) +inline void FreeDecalMeshData() +{ + GenMeshDecal((Model) { .meshCount = -1 }, (Matrix) { 0 }, 0.0f, 0.0f); +} static bool GuiButton(Rectangle rec, const char *label); //------------------------------------------------------------------------------------ @@ -198,12 +201,12 @@ int main(void) EndMode3D(); float yPos = 10; - float x0 = GetScreenWidth() - 300; + float x0 = GetScreenWidth() - 300.0f; float x1 = x0 + 100; float x2 = x1 + 100; - DrawText("Vertices", x1, yPos, 10, LIME); - DrawText("Triangles", x2, yPos, 10, LIME); + DrawText("Vertices", (int)x1, (int)yPos, 10, LIME); + DrawText("Triangles", (int)x2, (int)yPos, 10, LIME); yPos += 15; int vertexCount = 0; @@ -215,24 +218,24 @@ int main(void) triangleCount += model.meshes[i].triangleCount; } - DrawText("Main model", x0, yPos, 10, LIME); - DrawText(TextFormat("%d", vertexCount), x1, yPos, 10, LIME); - DrawText(TextFormat("%d", triangleCount), x2, yPos, 10, LIME); + DrawText("Main model", (int)x0, (int)yPos, 10, LIME); + DrawText(TextFormat("%d", vertexCount), (int)x1, (int)yPos, 10, LIME); + DrawText(TextFormat("%d", triangleCount), (int)x2, (int)yPos, 10, LIME); yPos += 15; for (int i = 0; i < decalCount; i++) { if (i == 20) { - DrawText("...", x0, yPos, 10, LIME); + DrawText("...", (int)x0, (int)yPos, 10, LIME); yPos += 15; } if (i < 20) { - DrawText(TextFormat("Decal #%d", i+1), x0, yPos, 10, LIME); - DrawText(TextFormat("%d", decalModels[i].meshes[0].vertexCount), x1, yPos, 10, LIME); - DrawText(TextFormat("%d", decalModels[i].meshes[0].triangleCount), x2, yPos, 10, LIME); + DrawText(TextFormat("Decal #%d", i+1), (int)x0, (int)yPos, 10, LIME); + DrawText(TextFormat("%d", decalModels[i].meshes[0].vertexCount), (int)x1, (int)yPos, 10, LIME); + DrawText(TextFormat("%d", decalModels[i].meshes[0].triangleCount), (int)x2, (int)yPos, 10, LIME); yPos += 15; } @@ -240,18 +243,18 @@ int main(void) triangleCount += decalModels[i].meshes[0].triangleCount; } - DrawText("TOTAL", x0, yPos, 10, LIME); - DrawText(TextFormat("%d", vertexCount), x1, yPos, 10, LIME); - DrawText(TextFormat("%d", triangleCount), x2, yPos, 10, LIME); + DrawText("TOTAL", (int)x0, (int)yPos, 10, LIME); + DrawText(TextFormat("%d", vertexCount), (int)x1, (int)yPos, 10, LIME); + DrawText(TextFormat("%d", triangleCount), (int)x2, (int)yPos, 10, LIME); yPos += 15; DrawText("Hold RMB to move camera", 10, 430, 10, GRAY); DrawText("(c) Character model and texture from kenney.nl", screenWidth - 260, screenHeight - 20, 10, GRAY); // UI elements - if (GuiButton((Rectangle){ 10, screenHeight - 100, 100, 60 }, showModel ? "Hide Model" : "Show Model")) showModel = !showModel; + if (GuiButton((Rectangle){ 10, screenHeight - 1000.f, 100, 60 }, showModel ? "Hide Model" : "Show Model")) showModel = !showModel; - if (GuiButton((Rectangle){ 10 + 110, screenHeight - 100, 100, 60 }, "Clear Decals")) + if (GuiButton((Rectangle){ 10 + 110, screenHeight - 100.0f, 100, 60 }, "Clear Decals")) { // Clear decals, unload all decal models for (int i = 0; i < decalCount; i++) UnloadModel(decalModels[i]); @@ -596,8 +599,8 @@ static bool GuiButton(Rectangle rec, const char *label) DrawRectangleRec(rec, bgColor); DrawRectangleLinesEx(rec, 2.0f, DARKGRAY); - float fontSize = 10.0f; - float textWidth = MeasureText(label, fontSize); + int fontSize = 10; + int textWidth = MeasureText(label, fontSize); DrawText(label, (int)(rec.x + rec.width*0.5f - textWidth*0.5f), (int)(rec.y + rec.height*0.5f - fontSize*0.5f), fontSize, DARKGRAY); diff --git a/examples/shapes/shapes_ball_physics.c b/examples/shapes/shapes_ball_physics.c index f9b620d28..0c98ccf9d 100644 --- a/examples/shapes/shapes_ball_physics.c +++ b/examples/shapes/shapes_ball_physics.c @@ -46,12 +46,12 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shapes] example - ball physics"); Ball balls[MAX_BALLS] = {{ - .pos = { GetScreenWidth()/2, GetScreenHeight()/2 }, + .pos = { GetScreenWidth()/2.0f, GetScreenHeight()/2.0f }, .vel = { 200, 200 }, .ppos = { 0 }, .radius = 40, - .friction = 0.99, - .elasticity = 0.9, + .friction = 0.99f, + .elasticity = 0.9f, .color = BLUE, .grabbed = false }}; @@ -110,11 +110,11 @@ int main(void) { balls[ballCount++] = (Ball){ .pos = mousePos, - .vel = { GetRandomValue(-300, 300), GetRandomValue(-300, 300) }, + .vel = { (float)GetRandomValue(-300, 300), (float)GetRandomValue(-300, 300) }, .ppos = { 0 }, - .radius = 20 + GetRandomValue(0, 30), - .friction = 0.99, - .elasticity = 0.9, + .radius = 20.0f + (float)GetRandomValue(0, 30), + .friction = 0.99f, + .elasticity = 0.9f, .color = { GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }, .grabbed = false }; @@ -126,7 +126,7 @@ int main(void) { for (int i = 0; i < ballCount; i++) { - if (!balls[i].grabbed) balls[i].vel = (Vector2){ GetRandomValue(-2000, 2000), GetRandomValue(-2000, 2000) }; + if (!balls[i].grabbed) balls[i].vel = (Vector2){ (float)GetRandomValue(-2000, 2000), (float)GetRandomValue(-2000, 2000) }; } } diff --git a/examples/shapes/shapes_kaleidoscope.c b/examples/shapes/shapes_kaleidoscope.c index 119fca598..31129a54c 100644 --- a/examples/shapes/shapes_kaleidoscope.c +++ b/examples/shapes/shapes_kaleidoscope.c @@ -50,9 +50,9 @@ int main(void) int symmetry = 6; float angle = 360.0f/(float)symmetry; float thickness = 3.0f; - Rectangle resetButtonRec = { screenWidth - 55, 5, 50, 25 }; - Rectangle backButtonRec = { screenWidth - 55, screenHeight - 30, 25, 25 }; - Rectangle nextButtonRec = { screenWidth - 30, screenHeight - 30, 25, 25 }; + Rectangle resetButtonRec = { screenWidth - 55.0f, 5.0f, 50, 25 }; + Rectangle backButtonRec = { screenWidth - 55.0f, screenHeight - 30.0f, 25, 25 }; + Rectangle nextButtonRec = { screenWidth - 30.0f, screenHeight - 30.0f, 25, 25 }; Vector2 mousePos = { 0 }; Vector2 prevMousePos = { 0 }; Vector2 scaleVector = { 1.0f, -1.0f }; diff --git a/examples/shapes/shapes_penrose_tile.c b/examples/shapes/shapes_penrose_tile.c index 304dca3cc..cf41852f8 100644 --- a/examples/shapes/shapes_penrose_tile.c +++ b/examples/shapes/shapes_penrose_tile.c @@ -185,12 +185,12 @@ static void BuildProductionStep(PenroseLSystem *ls) char *newProduction = (char *)RL_MALLOC(sizeof(char)*STR_MAX_SIZE); newProduction[0] = '\0'; - int productionLength = strnlen(ls->production, STR_MAX_SIZE); + int productionLength = (int)strnlen(ls->production, STR_MAX_SIZE); for (int i = 0; i < productionLength; i++) { char step = ls->production[i]; - int remainingSpace = STR_MAX_SIZE - strnlen(newProduction, STR_MAX_SIZE) - 1; + int remainingSpace = STR_MAX_SIZE - (int)strnlen(newProduction, STR_MAX_SIZE) - 1; switch (step) { case 'W': strncat(newProduction, ls->ruleW, remainingSpace); break; @@ -201,7 +201,7 @@ static void BuildProductionStep(PenroseLSystem *ls) { if (step != 'F') { - int t = strnlen(newProduction, STR_MAX_SIZE); + int t = (int)strnlen(newProduction, STR_MAX_SIZE); newProduction[t] = step; newProduction[t + 1] = '\0'; } @@ -218,7 +218,7 @@ static void BuildProductionStep(PenroseLSystem *ls) // Draw penrose tile lines static void DrawPenroseLSystem(PenroseLSystem *ls) { - Vector2 screenCenter = { GetScreenWidth()/2, GetScreenHeight()/2 }; + Vector2 screenCenter = { GetScreenWidth()/2.0f, GetScreenHeight()/2.0f }; TurtleState turtle = { .origin = { 0 }, @@ -245,7 +245,7 @@ static void DrawPenroseLSystem(PenroseLSystem *ls) Vector2 startPosScreen = { startPosWorld.x + screenCenter.x, startPosWorld.y + screenCenter.y }; Vector2 endPosScreen = { turtle.origin.x + screenCenter.x, turtle.origin.y + screenCenter.y }; - DrawLineEx(startPosScreen, endPosScreen, 2, Fade(BLACK, 0.2)); + DrawLineEx(startPosScreen, endPosScreen, 2, Fade(BLACK, 0.2f)); } repeats = 1; diff --git a/examples/text/text_inline_styling.c b/examples/text/text_inline_styling.c index 8faef30eb..81f8156b6 100644 --- a/examples/text/text_inline_styling.c +++ b/examples/text/text_inline_styling.c @@ -178,14 +178,14 @@ static void DrawTextStyled(Font font, const char *text, Vector2 position, float // Convert hex color text into actual Color unsigned int colHexValue = strtoul(colHexText, NULL, 16); if (text[i - 1] == 'c') - { + { colFront = GetColor(colHexValue); - colFront.a *= (float)color.a/255.0f; + colFront.a = (unsigned char)(colFront.a * (float)color.a/255.0f); } else if (text[i - 1] == 'b') { colBack = GetColor(colHexValue); - colBack.a *= (float)color.a/255.0f; + colBack.a *= (unsigned char)(colFront.a * (float)color.a / 255.0f); } i += (colHexCount + 1); // Skip color value retrieved and ']' diff --git a/examples/text/text_strings_management.c b/examples/text/text_strings_management.c index d2e349279..e4a7ab2af 100644 --- a/examples/text/text_strings_management.c +++ b/examples/text/text_strings_management.c @@ -133,7 +133,7 @@ int main(void) { for (int i = 0; i < particleCount; i++) { - if (!textParticles[i].grabbed) textParticles[i].vel = (Vector2){ GetRandomValue(-2000, 2000), GetRandomValue(-2000, 2000) }; + if (!textParticles[i].grabbed) textParticles[i].vel = (Vector2){ (float)GetRandomValue(-2000, 2000), (float)GetRandomValue(-2000, 2000) }; } } @@ -233,9 +233,9 @@ int main(void) for (int i = 0; i < particleCount; i++) { TextParticle *tp = &textParticles[i]; - DrawRectangle(tp->rect.x-tp->borderWidth, tp->rect.y-tp->borderWidth, tp->rect.width+tp->borderWidth*2, tp->rect.height+tp->borderWidth*2, BLACK); + DrawRectangleRec((Rectangle) { tp->rect.x - tp->borderWidth, tp->rect.y - tp->borderWidth, tp->rect.width + tp->borderWidth * 2, tp->rect.height + tp->borderWidth * 2 }, BLACK); DrawRectangleRec(tp->rect, tp->color); - DrawText(tp->text, tp->rect.x+tp->padding, tp->rect.y+tp->padding, FONT_SIZE, BLACK); + DrawText(tp->text, (int)(tp->rect.x+tp->padding), (int)(tp->rect.y+tp->padding), FONT_SIZE, BLACK); } DrawText("grab a text particle by pressing with the mouse and throw it by releasing", 10, 10, 10, DARKGRAY); @@ -265,8 +265,8 @@ void PrepareFirstTextParticle(const char* text, TextParticle *tps, int *particle { tps[0] = CreateTextParticle( text, - GetScreenWidth()/2, - GetScreenHeight()/2, + GetScreenWidth()/2.0f, + GetScreenHeight()/2.0f, RAYWHITE ); *particleCount = 1; @@ -277,12 +277,12 @@ TextParticle CreateTextParticle(const char *text, float x, float y, Color color) TextParticle tp = { .text = "", .rect = { x, y, 30, 30 }, - .vel = { GetRandomValue(-200, 200), GetRandomValue(-200, 200) }, + .vel = { (float)GetRandomValue(-200, 200), (float)GetRandomValue(-200, 200) }, .ppos = { 0 }, .padding = 5.0f, .borderWidth = 5.0f, - .friction = 0.99, - .elasticity = 0.9, + .friction = 0.99f, + .elasticity = 0.9f, .color = color, .grabbed = false }; diff --git a/examples/textures/textures_screen_buffer.c b/examples/textures/textures_screen_buffer.c index e620aab31..503b8d249 100644 --- a/examples/textures/textures_screen_buffer.c +++ b/examples/textures/textures_screen_buffer.c @@ -66,7 +66,7 @@ int main(void) // Grow flameRoot for (int x = 2; x < flameWidth; x++) { - unsigned short flame = flameRootBuffer[x]; + unsigned char flame = flameRootBuffer[x]; if (flame == 255) continue; flame += GetRandomValue(0, 2); if (flame > 255) flame = 255; diff --git a/projects/VS2022/examples/shapes_hilbert_curve.vcxproj b/projects/VS2022/examples/shapes_hilbert_curve.vcxproj index 8fcbfab5f..6c60841fb 100644 --- a/projects/VS2022/examples/shapes_hilbert_curve.vcxproj +++ b/projects/VS2022/examples/shapes_hilbert_curve.vcxproj @@ -292,7 +292,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNIGNS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -309,7 +309,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNIGNS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -345,7 +345,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNIGNS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -366,7 +366,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNIGNS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -410,7 +410,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNIGNS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -432,7 +432,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNIGNS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -476,7 +476,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNIGNS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -504,7 +504,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNIGNS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/projects/VS2022/examples/shapes_penrose_tile.vcxproj b/projects/VS2022/examples/shapes_penrose_tile.vcxproj index bde99f8c1..389bdde36 100644 --- a/projects/VS2022/examples/shapes_penrose_tile.vcxproj +++ b/projects/VS2022/examples/shapes_penrose_tile.vcxproj @@ -292,7 +292,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -309,7 +309,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -345,7 +345,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -366,7 +366,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -410,7 +410,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -432,7 +432,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -476,7 +476,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -504,7 +504,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true diff --git a/projects/VS2022/examples/shapes_rlgl_color_wheel.vcxproj b/projects/VS2022/examples/shapes_rlgl_color_wheel.vcxproj index b22703577..a02a2d4e2 100644 --- a/projects/VS2022/examples/shapes_rlgl_color_wheel.vcxproj +++ b/projects/VS2022/examples/shapes_rlgl_color_wheel.vcxproj @@ -292,7 +292,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNIGNS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -309,7 +309,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNIGNS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) @@ -345,7 +345,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNIGNS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -366,7 +366,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNIGNS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) CompileAsC $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) @@ -410,7 +410,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNIGNS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -432,7 +432,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNIGNS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -476,7 +476,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNIGNS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true @@ -504,7 +504,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + _CRT_SECURE_NO_WARNIGNS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) CompileAsC true From a44157c2a834c4956e98afafbced96e25177b1a5 Mon Sep 17 00:00:00 2001 From: Jack Boakes <163384444+jackboakes@users.noreply.github.com> Date: Sun, 4 Jan 2026 08:41:52 +1100 Subject: [PATCH 074/117] [example] Added textures_frame_buffer_rendering (#5468) --- .../textures_frame_buffer_rendering.png | Bin 0 -> 31588 bytes .../textures/textures_framebuffer_rendering.c | 208 ++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 examples/textures/textures_frame_buffer_rendering.png create mode 100644 examples/textures/textures_framebuffer_rendering.c diff --git a/examples/textures/textures_frame_buffer_rendering.png b/examples/textures/textures_frame_buffer_rendering.png new file mode 100644 index 0000000000000000000000000000000000000000..e6829f0bd20ef747b29a0fb868ca5f2c4cc635e5 GIT binary patch literal 31588 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 Duq`3E literal 0 HcmV?d00001 diff --git a/examples/textures/textures_framebuffer_rendering.c b/examples/textures/textures_framebuffer_rendering.c new file mode 100644 index 000000000..a8b466187 --- /dev/null +++ b/examples/textures/textures_framebuffer_rendering.c @@ -0,0 +1,208 @@ +/******************************************************************************************* +* +* raylib [textures] example - framebuffer rendering +* +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 5.6, last time updated with raylib 5.6 +* +* Example contributed by Jack Boakes (@jackboakes) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2026-2026 Jack Boakes (@jackboakes) +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" + +//------------------------------------------------------------------------------------ +// Module Functions Declaration +//------------------------------------------------------------------------------------ +static void DrawCameraPrism(Camera3D camera, float aspect, Color color); + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + const int splitWidth = screenWidth/2; + + InitWindow(screenWidth, screenHeight, "raylib [textures] example - framebuffer rendering"); + + // Camera to look at the 3D world + Camera3D subjectCamera = { 0 }; + subjectCamera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; + subjectCamera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + subjectCamera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + subjectCamera.fovy = 45.0f; + subjectCamera.projection = CAMERA_PERSPECTIVE; + + // Camera to observe the subject camera and 3D world + Camera3D observerCamera = { 0 }; + observerCamera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; + observerCamera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + observerCamera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + observerCamera.fovy = 45.0f; + observerCamera.projection = CAMERA_PERSPECTIVE; + + // Set up render textures + RenderTexture2D observerTarget = LoadRenderTexture(splitWidth, screenHeight); + Rectangle observerSource = { 0.0f, 0.0f, (float)observerTarget.texture.width, -(float)observerTarget.texture.height }; + Rectangle observerDest = { 0.0f, 0.0f, (float)splitWidth, (float)screenHeight }; + + RenderTexture2D subjectTarget = LoadRenderTexture(splitWidth, screenHeight); + Rectangle subjectSource = { 0.0f, 0.0f, (float)subjectTarget.texture.width, -(float)subjectTarget.texture.height }; + Rectangle subjectDest = { (float)splitWidth, 0.0f, (float)splitWidth, (float)screenHeight }; + const float textureAspectRatio = (float)subjectTarget.texture.width/(float)subjectTarget.texture.height; + + // Rectangles for cropping render texture + const float captureSize = 128.0f; + Rectangle cropSource = { (subjectTarget.texture.width - captureSize)/2.0f, (subjectTarget.texture.height - captureSize)/2.0f, captureSize, -captureSize }; + Rectangle cropDest = { splitWidth + 20, 20, captureSize, captureSize}; + + SetTargetFPS(60); + DisableCursor(); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&observerCamera, CAMERA_FREE); + UpdateCamera(&subjectCamera, CAMERA_ORBITAL); + + if (IsKeyPressed(KEY_R)) observerCamera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + + // Build LHS observer view texture + BeginTextureMode(observerTarget); + + ClearBackground(RAYWHITE); + + BeginMode3D(observerCamera); + + DrawGrid(10, 1.0f); + DrawCube((Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, 2.0f, 2.0f, GOLD); + DrawCubeWires((Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, 2.0f, 2.0f, PINK); + DrawCameraPrism(subjectCamera, textureAspectRatio, GREEN); + + EndMode3D(); + + DrawText("Observer View", 10, observerTarget.texture.height - 30, 20, BLACK); + DrawText("WASD + Mouse to Move", 10, 10, 20, DARKGRAY); + DrawText("Scroll to Zoom", 10, 30, 20, DARKGRAY); + DrawText("R to Reset Observer Target", 10, 50, 20, DARKGRAY); + + EndTextureMode(); + + // Build RHS subject view texture + BeginTextureMode(subjectTarget); + + ClearBackground(RAYWHITE); + + BeginMode3D(subjectCamera); + + DrawCube((Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, 2.0f, 2.0f, GOLD); + DrawCubeWires((Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, 2.0f, 2.0f, PINK); + DrawGrid(10, 1.0f); + + EndMode3D(); + + DrawRectangleLines((subjectTarget.texture.width - captureSize)/2, (subjectTarget.texture.height - captureSize)/2, captureSize, captureSize, GREEN); + DrawText("Subject View", 10, subjectTarget.texture.height - 30, 20, BLACK); + + EndTextureMode(); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(BLACK); + + // Draw observer texture LHS + DrawTexturePro(observerTarget.texture, observerSource, observerDest, (Vector2){0.0f, 0.0f }, 0.0f, WHITE); + + // Draw subject texture RHS + DrawTexturePro(subjectTarget.texture, subjectSource, subjectDest, (Vector2){ 0.0f, 0.0f }, 0.0f, WHITE); + + // Draw the small crop overlay on top + DrawTexturePro(subjectTarget.texture, cropSource, cropDest, (Vector2){ 0.0f, 0.0f }, 0.0f, WHITE); + DrawRectangleLinesEx(cropDest, 2, BLACK); + + // Draw split screen divider line + DrawLine(splitWidth, 0, splitWidth, screenHeight, BLACK); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadRenderTexture(observerTarget); + UnloadRenderTexture(subjectTarget); + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- +static void DrawCameraPrism(Camera3D camera, float aspect, Color color) +{ + float length = Vector3Distance(camera.position, camera.target); + // Define the 4 corners of the camera's prism plane sliced at the target in Normalized Device Coordinates + Vector3 planeNDC[4] = { + { -1.0f, -1.0f, 1.0f }, // Bottom Left + { 1.0f, -1.0f, 1.0f }, // Bottom Right + { 1.0f, 1.0f, 1.0f }, // Top Right + { -1.0f, 1.0f, 1.0f } // Top Left + }; + + // Build the matrices + Matrix view = GetCameraMatrix(camera); + Matrix proj = MatrixPerspective(camera.fovy * DEG2RAD, aspect, 0.05f, length); + // Combine view and projection so we can reverse the full camera transform + Matrix viewProj = MatrixMultiply(view, proj); + // Invert the view-projection matrix to unproject points from NDC space back into world space + Matrix inverseViewProj = MatrixInvert(viewProj); + + // Transform the 4 plane corners from NDC into world space + Vector3 corners[4]; + for (int i = 0; i < 4; i++) + { + float x = planeNDC[i].x; + float y = planeNDC[i].y; + float z = planeNDC[i].z; + + // Multiply NDC position by the inverse view-projection matrix + // This produces a homogeneous (x, y, z, w) position in world space + float vx = inverseViewProj.m0*x + inverseViewProj.m4*y + inverseViewProj.m8*z + inverseViewProj.m12; + float vy = inverseViewProj.m1*x + inverseViewProj.m5*y + inverseViewProj.m9*z + inverseViewProj.m13; + float vz = inverseViewProj.m2*x + inverseViewProj.m6*y + inverseViewProj.m10*z + inverseViewProj.m14; + float vw = inverseViewProj.m3*x + inverseViewProj.m7*y + inverseViewProj.m11*z + inverseViewProj.m15; + + corners[i] = (Vector3){ vx/vw, vy/vw, vz/vw }; + } + + // Draw the far plane sliced at the target + DrawLine3D(corners[0], corners[1], color); + DrawLine3D(corners[1], corners[2], color); + DrawLine3D(corners[2], corners[3], color); + DrawLine3D(corners[3], corners[0], color); + + // Draw the prism lines from the far plane to the camera position + for (int i = 0; i < 4; i++) + { + DrawLine3D(camera.position, corners[i], color); + } +} \ No newline at end of file From c4b11a30cd77d8cbcf3380cecf3f3540257caaf9 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Sat, 3 Jan 2026 13:52:04 -0800 Subject: [PATCH 075/117] Fix DrawMeshInstanced breaking if instanceTransform is unused (#5469) --- src/rmodels.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 3ee429900..c22c0a0c9 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1762,11 +1762,14 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i instancesVboId = rlLoadVertexBuffer(instanceTransforms, instances*sizeof(float16), false); // Instances transformation matrices are sent to shader attribute location: SHADER_LOC_VERTEX_INSTANCE_TX - for (unsigned int i = 0; i < 4; i++) + if (material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] != -1) { - rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] + i); - rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] + i, 4, RL_FLOAT, 0, sizeof(Matrix), i*sizeof(Vector4)); - rlSetVertexAttributeDivisor(material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] + i, 1); + for (unsigned int i = 0; i < 4; i++) + { + rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] + i); + rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] + i, 4, RL_FLOAT, 0, sizeof(Matrix), i*sizeof(Vector4)); + rlSetVertexAttributeDivisor(material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] + i, 1); + } } rlDisableVertexBuffer(); From af544c24b9751a4b2972e22e472578cfc30ff339 Mon Sep 17 00:00:00 2001 From: ssszcmawo Date: Sat, 3 Jan 2026 22:57:22 +0100 Subject: [PATCH 076/117] [rcore] Fix touch position automation event handling (#5470) * fix touch position automation event handling * Fix alignment of previousPosition comment in rcore.c --- src/rcore.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index e16c6412a..59563537d 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -350,11 +350,12 @@ typedef struct CoreData { } Mouse; struct { - int pointCount; // Number of touch points active - int pointId[MAX_TOUCH_POINTS]; // Point identifiers - Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen - char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state - char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state + int pointCount; // Number of touch points active + int pointId[MAX_TOUCH_POINTS]; // Point identifiers + Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen + Vector2 previousPosition[MAX_TOUCH_POINTS]; // Previous touch position on screen + char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state + char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state } Touch; struct { @@ -4104,22 +4105,20 @@ static void RecordAutomationEvent(void) if (currentEventList->count == currentEventList->capacity) return; // Security check - // Event type: INPUT_TOUCH_POSITION - // TODO: It requires the id! - /* - if (((int)CORE.Input.Touch.currentPosition[id].x != (int)CORE.Input.Touch.previousPosition[id].x) || - ((int)CORE.Input.Touch.currentPosition[id].y != (int)CORE.Input.Touch.previousPosition[id].y)) + // Event type: INPUT_TOUCH_POSITION + if (((int)CORE.Input.Touch.position[id].x != (int)CORE.Input.Touch.previousPosition[id].x) || + ((int)CORE.Input.Touch.position[id].y != (int)CORE.Input.Touch.previousPosition[id].y)) { currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; currentEventList->events[currentEventList->count].type = INPUT_TOUCH_POSITION; currentEventList->events[currentEventList->count].params[0] = id; - currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Touch.currentPosition[id].x; - currentEventList->events[currentEventList->count].params[2] = (int)CORE.Input.Touch.currentPosition[id].y; + currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Touch.position[id].x; + currentEventList->events[currentEventList->count].params[2] = (int)CORE.Input.Touch.position[id].y; TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_POSITION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - */ + if (currentEventList->count == currentEventList->capacity) return; // Security check } From 35fc8ece44f899b420c7a227b2f307ba48ff48d2 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 3 Jan 2026 23:01:37 +0100 Subject: [PATCH 077/117] Update models_decals.c --- examples/models/models_decals.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/models/models_decals.c b/examples/models/models_decals.c index f35794daa..71122dd68 100644 --- a/examples/models/models_decals.c +++ b/examples/models/models_decals.c @@ -45,10 +45,7 @@ static void FreeMeshBuilder(MeshBuilder *mb); static Mesh BuildMesh(MeshBuilder *mb); static Mesh GenMeshDecal(Model inputModel, Matrix projection, float decalSize, float decalOffset); static Vector3 ClipSegment(Vector3 v0, Vector3 v1, Vector3 p, float s); -inline void FreeDecalMeshData() -{ - GenMeshDecal((Model) { .meshCount = -1 }, (Matrix) { 0 }, 0.0f, 0.0f); -} +static void FreeDecalMeshData(void) { GenMeshDecal((Model){ .meshCount = -1 }, (Matrix){ 0 }, 0.0f, 0.0f); } static bool GuiButton(Rectangle rec, const char *label); //------------------------------------------------------------------------------------ From 3678c2d15763c4ec906506ba9f9e790ede8d0b94 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 5 Jan 2026 20:47:25 +0100 Subject: [PATCH 078/117] REMOVE: `TRACELOGD()`, hardly ever used --- src/config.h | 2 -- src/platforms/rcore_web.c | 8 ++++---- src/platforms/rcore_web_emscripten.c | 8 ++++---- src/rlgl.h | 5 ++--- src/rtext.c | 2 +- src/rtextures.c | 4 ++-- src/utils.h | 7 ------- 7 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/config.h b/src/config.h index 9a1d22de3..1286e5082 100644 --- a/src/config.h +++ b/src/config.h @@ -287,9 +287,7 @@ // Standard file io library (stdio.h) included #define SUPPORT_STANDARD_FILEIO 1 // Show TRACELOG() output messages -// NOTE: By default LOG_DEBUG traces not shown #define SUPPORT_TRACELOG 1 -//#define SUPPORT_TRACELOG_DEBUG 1 // utils: Configuration values //------------------------------------------------------------------------------------ diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 0056849dd..2b51804ef 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -1104,7 +1104,7 @@ void PollInputEvents(void) else CORE.Input.Gamepad.currentButtonState[i][button] = 0; } - //TRACELOGD("INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]); + //TRACELOG(LOG_DEBUG, "INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]); } // Register axis data for every connected gamepad @@ -1695,12 +1695,12 @@ static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPoin static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) { /* - TRACELOGD("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"", + TRACELOG(LOG_DEBUG, "%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"", eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state", gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping); - for (int i = 0; i < gamepadEvent->numAxes; i++) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]); - for (int i = 0; i < gamepadEvent->numButtons; i++) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); + for (int i = 0; i < gamepadEvent->numAxes; i++) TRACELOG(LOG_DEBUG, "Axis %d: %g", i, gamepadEvent->axis[i]); + for (int i = 0; i < gamepadEvent->numButtons; i++) TRACELOG(LOG_DEBUG, "Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); */ if (gamepadEvent->connected && (gamepadEvent->index < MAX_GAMEPADS)) diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c index 28d530e97..5fdcdbefc 100644 --- a/src/platforms/rcore_web_emscripten.c +++ b/src/platforms/rcore_web_emscripten.c @@ -1076,7 +1076,7 @@ void PollInputEvents(void) else CORE.Input.Gamepad.currentButtonState[i][button] = 0; } - //TRACELOGD("INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]); + //TRACELOG(LOG_DEBUG, "INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]); } // Register axis data for every connected gamepad @@ -1586,12 +1586,12 @@ static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPoin static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) { /* - TRACELOGD("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"", + TRACELOG(LOG_DEBUG, "%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"", eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state", gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping); - for (int i = 0; i < gamepadEvent->numAxes; i++) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]); - for (int i = 0; i < gamepadEvent->numButtons; i++) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); + for (int i = 0; i < gamepadEvent->numAxes; i++) TRACELOG(LOG_DEBUG, "Axis %d: %g", i, gamepadEvent->axis[i]); + for (int i = 0; i < gamepadEvent->numButtons; i++) TRACELOG(LOG_DEBUG, "Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); */ if (gamepadEvent->connected && (gamepadEvent->index < MAX_GAMEPADS)) diff --git a/src/rlgl.h b/src/rlgl.h index ab85569bb..7fa22f9cc 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -132,7 +132,6 @@ // Support TRACELOG macros #ifndef TRACELOG #define TRACELOG(level, ...) (void)0 - #define TRACELOGD(...) (void)0 #endif // Allow custom memory allocators @@ -3324,7 +3323,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, unsigned int glInternalFormat, glFormat, glType; rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - TRACELOGD("TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); + TRACELOG(RL_LOG_DEBUG, "TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); if (glInternalFormat != 0) { @@ -4246,7 +4245,7 @@ unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode) glGetActiveUniform(id, i, sizeof(name) - 1, &namelen, &num, &type, name); name[namelen] = 0; - TRACELOGD("SHADER: [ID %i] Active uniform (%s) set at location: %i", id, name, glGetUniformLocation(id, name)); + TRACELOG(RL_LOG_DEBUG, "SHADER: [ID %i] Active uniform (%s) set at location: %i", id, name, glGetUniformLocation(id, name)); } } */ diff --git a/src/rtext.c b/src/rtext.c index 1fd9a306d..8413d62bf 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1035,7 +1035,7 @@ void UnloadFont(Font font) UnloadTexture(font.texture); RL_FREE(font.recs); - TRACELOGD("FONT: Unloaded font data from RAM and VRAM"); + TRACELOG(LOG_DEBUG, "FONT: Unloaded font data from RAM and VRAM"); } } diff --git a/src/rtextures.c b/src/rtextures.c index 59000940e..8d4128d4e 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2395,7 +2395,7 @@ void ImageMipmaps(Image *image) if (mipWidth < 1) mipWidth = 1; if (mipHeight < 1) mipHeight = 1; - TRACELOGD("IMAGE: Next mipmap level: %i x %i - current size %i", mipWidth, mipHeight, mipSize); + TRACELOG(LOG_DEBUG, "IMAGE: Next mipmap level: %i x %i - current size %i", mipWidth, mipHeight, mipSize); mipCount++; mipSize += GetPixelDataSize(mipWidth, mipHeight, image->format); // Add mipmap size (in bytes) @@ -2432,7 +2432,7 @@ void ImageMipmaps(Image *image) if (i < image->mipmaps) continue; - TRACELOGD("IMAGE: Generating mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip); + TRACELOG(LOG_DEBUG, "IMAGE: Generating mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip); ImageResize(&imCopy, mipWidth, mipHeight); // Uses internally Mitchell cubic downscale filter memcpy(nextmip, imCopy.data, mipSize); } diff --git a/src/utils.h b/src/utils.h index 7d79c2188..9c15ac285 100644 --- a/src/utils.h +++ b/src/utils.h @@ -34,15 +34,8 @@ #if defined(SUPPORT_TRACELOG) #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) - - #if defined(SUPPORT_TRACELOG_DEBUG) - #define TRACELOGD(...) TraceLog(LOG_DEBUG, __VA_ARGS__) - #else - #define TRACELOGD(...) (void)0 - #endif #else #define TRACELOG(level, ...) (void)0 - #define TRACELOGD(...) (void)0 #endif //---------------------------------------------------------------------------------- From c78ac657862e13898b4bc3a3b8a200a61667042c Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Tue, 6 Jan 2026 13:32:42 -0800 Subject: [PATCH 079/117] Don't require a M3d animation only file to have a mesh. There are valid use cases for animation only files that can be applied to N other meshes. (#5475) --- src/rmodels.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index c22c0a0c9..20287a935 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -7045,8 +7045,8 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i animations, %i bones, %i skins", fileName, m3d->numaction, m3d->numbone, m3d->numskin); - // No animation or bone+skin? - if (!m3d->numaction || !m3d->numbone || !m3d->numskin) + // No animation or bones, exit out. skins are not required because some people use one animation for N models + if (!m3d->numaction || !m3d->numbone) { m3d_free(m3d); UnloadFileData(fileData); From 23bc037c37545d6bcbf31231d00503339234fabf Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 7 Jan 2026 22:32:09 +0100 Subject: [PATCH 080/117] Revert change, trying to follow DRM implementation but not needed on Android #5477 --- src/platforms/rcore_android.c | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 6d3d68f24..19b686cef 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -919,26 +919,7 @@ static int InitGraphicsDevice(void) EGLint numConfigs = 0; // Get an EGL device connection - // NOTE: eglGetPlatformDisplay() is preferred over eglGetDisplay() legacy call - platform.device = EGL_NO_DISPLAY; -#if defined(EGL_VERSION_1_5) - platform.device = eglGetPlatformDisplay(EGL_PLATFORM_GBM_KHR, platform.gbmDevice, NULL); -#else - // Check if extension is available for eglGetPlatformDisplayEXT() - // NOTE: Better compatibility with some drivers (e.g. Mali Midgard) - const char *eglClientExtensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); - if (eglClientExtensions != NULL) - { - if (strstr(eglClientExtensions, "EGL_EXT_platform_base") != NULL) - { - PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); - if (eglGetPlatformDisplayEXT != NULL) platform.device = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, platform.gbmDevice, NULL); - } - } - - // In case extension not found or display could not be retrieved, try useing legacy version - if (platform.device == EGL_NO_DISPLAY) platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY); -#endif + platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (platform.device == EGL_NO_DISPLAY) { From c256f146b4c4e6b2f796bcb9e86197be44ec57e8 Mon Sep 17 00:00:00 2001 From: ssszcmawo Date: Wed, 7 Jan 2026 22:32:58 +0100 Subject: [PATCH 081/117] added saving to memory buffer and SaveFileData for binary files (#5476) --- src/rcore.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 59563537d..52ca0e2b6 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3230,15 +3230,25 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName) #if defined(SUPPORT_AUTOMATION_EVENTS) // Export events as binary file - // TODO: Save to memory buffer and SaveFileData() - /* - unsigned char fileId[4] = "rAE "; - FILE *raeFile = fopen(fileName, "wb"); - fwrite(fileId, sizeof(unsigned char), 4, raeFile); - fwrite(&eventCount, sizeof(int), 1, raeFile); - fwrite(events, sizeof(AutomationEvent), eventCount, raeFile); - fclose(raeFile); - */ + + // Binary buffer size = header (file id + count) + events data + int binarySize = 4 + sizeof(int) + sizeof(AutomationEvent)*list.count; + unsigned char *binBuffer = (unsigned char* )RL_MALLOC(binarySize); + if(!binBuffer) return false; + + int offset = 0; + memcpy(binBuffer + offset, "rAE ", 4); offset += 4; + memcpy(binBuffer + offset, &list.count, sizeof(int)); offset += sizeof(int); + + if(list.count > 0) + { + memcpy(binBuffer + offset, list.events,sizeof(AutomationEvent)*list.count); + offset += sizeof(AutomationEvent)*list.count; + } + + success = SaveFileData(TextFormat("%s.rae",fileName), binBuffer, binarySize); + + RL_FREE(binBuffer); // Export events as text // NOTE: Save to memory buffer and SaveFileText() From 229f82699ba311f8ca71c977d1eb6ae3ca2b6597 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 7 Jan 2026 22:36:40 +0100 Subject: [PATCH 082/117] Reviewed change #5476 --- src/rcore.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 52ca0e2b6..b7fcbf4a0 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3230,25 +3230,24 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName) #if defined(SUPPORT_AUTOMATION_EVENTS) // Export events as binary file - - // Binary buffer size = header (file id + count) + events data - int binarySize = 4 + sizeof(int) + sizeof(AutomationEvent)*list.count; - unsigned char *binBuffer = (unsigned char* )RL_MALLOC(binarySize); - if(!binBuffer) return false; - - int offset = 0; - memcpy(binBuffer + offset, "rAE ", 4); offset += 4; - memcpy(binBuffer + offset, &list.count, sizeof(int)); offset += sizeof(int); - - if(list.count > 0) + // NOTE: Code not used, only for reference if required in the future + /* + if (list.count > 0) { - memcpy(binBuffer + offset, list.events,sizeof(AutomationEvent)*list.count); + int binarySize = 4 + sizeof(int) + sizeof(AutomationEvent)*list.count; + unsigned char *binBuffer = (unsigned char *)RL_CALLOC(binarySize, 1); + int offset = 0; + memcpy(binBuffer + offset, "rAE ", 4); + offset += 4; + memcpy(binBuffer + offset, &list.count, sizeof(int)); + offset += sizeof(int); + memcpy(binBuffer + offset, list.events, sizeof(AutomationEvent)*list.count); offset += sizeof(AutomationEvent)*list.count; + + success = SaveFileData(TextFormat("%s.rae",fileName), binBuffer, binarySize); + RL_FREE(binBuffer); } - - success = SaveFileData(TextFormat("%s.rae",fileName), binBuffer, binarySize); - - RL_FREE(binBuffer); + */ // Export events as text // NOTE: Save to memory buffer and SaveFileText() From 5e1f5d5b7429bb7d1dae2be9a99266b73de954d4 Mon Sep 17 00:00:00 2001 From: Paul de Mascarel Date: Wed, 7 Jan 2026 22:38:04 +0100 Subject: [PATCH 083/117] examples/models: optimize collision check in first_person_maze (#5478) Limit collision detection to the player surrounding cells instead of iterating the full cubicmap each frame. --- examples/models/models_first_person_maze.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/models/models_first_person_maze.c b/examples/models/models_first_person_maze.c index 43020974f..eb0db6024 100644 --- a/examples/models/models_first_person_maze.c +++ b/examples/models/models_first_person_maze.c @@ -80,12 +80,13 @@ int main(void) if (playerCellY < 0) playerCellY = 0; else if (playerCellY >= cubicmap.height) playerCellY = cubicmap.height - 1; - // Check map collisions using image data and player position - // TODO: Improvement: Just check player surrounding cells for collision - for (int y = 0; y < cubicmap.height; y++) + // Check map collisions using image data and player position against surrounding cells only + for (int y = playerCellY - 1; y <= playerCellY + 1; y++) { - for (int x = 0; x < cubicmap.width; x++) + if (y < 0 || y >= cubicmap.height) continue; + for (int x = playerCellX - 1; x <= playerCellX + 1; x++) { + if (x < 0 || x >= cubicmap.width) continue; if ((mapPixels[y*cubicmap.width + x].r == 255) && // Collision: white pixel, only check R channel (CheckCollisionCircleRec(playerPos, playerRadius, (Rectangle){ mapPosition.x - 0.5f + x*1.0f, mapPosition.z - 0.5f + y*1.0f, 1.0f, 1.0f }))) From c814625c009f7bdbca47352a5951236558937e7e Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 7 Jan 2026 22:40:28 +0100 Subject: [PATCH 084/117] Update models_first_person_maze.c --- examples/models/models_first_person_maze.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/models/models_first_person_maze.c b/examples/models/models_first_person_maze.c index eb0db6024..4c77d6121 100644 --- a/examples/models/models_first_person_maze.c +++ b/examples/models/models_first_person_maze.c @@ -83,10 +83,14 @@ int main(void) // Check map collisions using image data and player position against surrounding cells only for (int y = playerCellY - 1; y <= playerCellY + 1; y++) { - if (y < 0 || y >= cubicmap.height) continue; + // Avoid map accessing out of bounds + if ((y < 0) || (y >= cubicmap.height)) continue; + for (int x = playerCellX - 1; x <= playerCellX + 1; x++) { - if (x < 0 || x >= cubicmap.width) continue; + // Avoid map accessing out of bounds + if ((x < 0) || (x >= cubicmap.width)) continue; + if ((mapPixels[y*cubicmap.width + x].r == 255) && // Collision: white pixel, only check R channel (CheckCollisionCircleRec(playerPos, playerRadius, (Rectangle){ mapPosition.x - 0.5f + x*1.0f, mapPosition.z - 0.5f + y*1.0f, 1.0f, 1.0f }))) From 5398b8c9b06589ee3897f6c17c8739a4260d3f86 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 7 Jan 2026 23:09:51 +0100 Subject: [PATCH 085/117] Update rexm.c --- tools/rexm/rexm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index d3ff18289..9914733f0 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -2894,8 +2894,8 @@ static void UpdateWebMetadata(const char *exHtmlPath, const char *exFilePath) // Get example name: replace underscore by spaces strncpy(exName, GetFileNameWithoutExt(exHtmlPathCopy), 64 - 1); - strncpy(exTitle, exName, 64 - 1); - for (int i = 0; (i < 256) && (exTitle[i] != '\0'); i++) { if (exTitle[i] == '_') exTitle[i] = ' '; } + strcpy(exTitle, exName); + for (int i = 0; (i < 64) && (exTitle[i] != '\0'); i++) { if (exTitle[i] == '_') exTitle[i] = ' '; } // Get example category from exName: copy until first underscore for (int i = 0; (exName[i] != '_'); i++) exCategory[i] = exName[i]; From 0bcf79ce287d6180cedf7422c8a947e61247e81e Mon Sep 17 00:00:00 2001 From: Krzysztof Szenk Date: Thu, 8 Jan 2026 17:26:56 +0100 Subject: [PATCH 086/117] [#5455] Fix for: Fix window width calculation by adding wOffset (#5480) --- src/external/RGFW.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/external/RGFW.h b/src/external/RGFW.h index 0ab3858cf..c01ce1177 100644 --- a/src/external/RGFW.h +++ b/src/external/RGFW.h @@ -669,7 +669,8 @@ typedef struct RGFW_event { typedef struct RGFW_window_src { HWND window; /*!< source window */ HDC hdc; /*!< source HDC */ - u32 hOffset; /*!< height offset for window */ + i32 wOffset; /*!< width offset for window */ + i32 hOffset; /*!< height offset for window */ HICON hIconSmall, hIconBig; /*!< source window icons */ #if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL) HGLRC ctx; /*!< source graphics context */ @@ -6537,11 +6538,11 @@ LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) else windowRect.bottom = windowRect.top + newHeight; } - RGFW_window_resize(win, RGFW_AREA((windowRect.right - windowRect.left), + RGFW_window_resize(win, RGFW_AREA((u32)(windowRect.right - windowRect.left) - (u32)win->src.wOffset, (u32)(windowRect.bottom - windowRect.top) - (u32)win->src.hOffset)); } - win->r.w = windowRect.right - windowRect.left; + win->r.w = (windowRect.right - windowRect.left) - (i32)win->src.wOffset; win->r.h = (windowRect.bottom - windowRect.top) - (i32)win->src.hOffset; RGFW_eventQueuePushEx(e.type = RGFW_windowResized; e._win = win); RGFW_windowResizedCallback(win, win->r); @@ -6561,12 +6562,12 @@ LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) #endif case WM_GETMINMAXINFO: { MINMAXINFO* mmi = (MINMAXINFO*) lParam; - mmi->ptMinTrackSize.x = (LONG)win->src.minSize.w; + mmi->ptMinTrackSize.x = (LONG)(win->src.minSize.w + win->src.wOffset); mmi->ptMinTrackSize.y = (LONG)(win->src.minSize.h + win->src.hOffset); if (win->src.maxSize.w == 0 && win->src.maxSize.h == 0) return DefWindowProcW(hWnd, message, wParam, lParam); - mmi->ptMaxTrackSize.x = (LONG)win->src.maxSize.w; + mmi->ptMaxTrackSize.x = (LONG)(win->src.maxSize.w + win->src.wOffset); mmi->ptMaxTrackSize.y = (LONG)(win->src.maxSize.h + win->src.hOffset); return DefWindowProcW(hWnd, message, wParam, lParam); } @@ -6969,7 +6970,7 @@ RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowF win->src.hOffset = (u32)(windowRect.bottom - windowRect.top) - (u32)(clientRect.bottom - clientRect.top); win->src.wOffset = (u32)(windowRect.right - windowRect.left) - (u32)(clientRect.right - clientRect.left); - win->src.window = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->r.x, win->r.y, win->r.w, win->r.h + (i32)win->src.hOffset, 0, 0, inh, 0); + win->src.window = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->r.x, win->r.y, win->r.w + (i32)win->src.wOffset, win->r.h + (i32)win->src.hOffset, 0, 0, inh, 0); SetPropW(win->src.window, L"RGFW", win); RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h)); /* so WM_GETMINMAXINFO gets called again */ @@ -7065,7 +7066,7 @@ void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { if (fullscreen == RGFW_FALSE) { RGFW_window_setBorder(win, 1); - SetWindowPos(win->src.window, HWND_NOTOPMOST, win->_oldRect.x, win->_oldRect.y, win->_oldRect.w, win->_oldRect.h + (i32)win->src.hOffset, + SetWindowPos(win->src.window, HWND_NOTOPMOST, win->_oldRect.x, win->_oldRect.y, win->_oldRect.w + (i32)win->src.wOffset, win->_oldRect.h + (i32)win->src.hOffset, SWP_NOOWNERZORDER | SWP_FRAMECHANGED); win->_flags &= ~(u32)RGFW_windowFullscreen; @@ -7899,7 +7900,7 @@ void RGFW_window_resize(RGFW_window* win, RGFW_area a) { win->r.w = (i32)a.w; win->r.h = (i32)a.h; - SetWindowPos(win->src.window, HWND_TOP, 0, 0, win->r.w, win->r.h + (i32)win->src.hOffset, SWP_NOMOVE); + SetWindowPos(win->src.window, HWND_TOP, 0, 0, win->r.w + (i32)win->src.wOffset, win->r.h + (i32)win->src.hOffset, SWP_NOMOVE); } From 16e6d325b9f787bb99dc11c6504f8b73d37706ac Mon Sep 17 00:00:00 2001 From: Marcos Paccor Date: Thu, 8 Jan 2026 18:04:09 -0300 Subject: [PATCH 087/117] [raudio] Fix freeing the wrong memory (#5481) --- src/raudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index 98326727f..cfb86cdbd 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1249,7 +1249,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) frameCount = (ma_uint32)ma_convert_frames(data, frameCount, formatOut, channels, sampleRate, wave->data, frameCountIn, formatIn, wave->channels, wave->sampleRate); if (frameCount == 0) { - RL_FREE(wave->data); + RL_FREE(data); TRACELOG(LOG_WARNING, "WAVE: Failed format conversion"); return; } From 5cc42c1b805cd07227d48ee73c9c21c35fb528aa Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 9 Jan 2026 19:55:26 +0100 Subject: [PATCH 088/117] Updated file name --- examples/textures/textures_framebuffer_rendering.c | 2 +- ...ering.png => textures_framebuffer_rendering.png} | Bin 2 files changed, 1 insertion(+), 1 deletion(-) rename examples/textures/{textures_frame_buffer_rendering.png => textures_framebuffer_rendering.png} (100%) diff --git a/examples/textures/textures_framebuffer_rendering.c b/examples/textures/textures_framebuffer_rendering.c index a8b466187..484192739 100644 --- a/examples/textures/textures_framebuffer_rendering.c +++ b/examples/textures/textures_framebuffer_rendering.c @@ -11,7 +11,7 @@ * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * -* Copyright (c) 2026-2026 Jack Boakes (@jackboakes) +* Copyright (c) 2026 Jack Boakes (@jackboakes) * ********************************************************************************************/ diff --git a/examples/textures/textures_frame_buffer_rendering.png b/examples/textures/textures_framebuffer_rendering.png similarity index 100% rename from examples/textures/textures_frame_buffer_rendering.png rename to examples/textures/textures_framebuffer_rendering.png From 4cf844b74ea9a3c5e5ab6cd57b4b7ff1eafe275b Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 9 Jan 2026 19:55:31 +0100 Subject: [PATCH 089/117] Update raylib.h --- src/raylib.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 2d411f896..6eae8411e 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -570,8 +570,7 @@ typedef enum { } TraceLogLevel; // Keyboard keys (US keyboard layout) -// NOTE: Use GetKeyPressed() to allow redefining -// required keys for alternative layouts +// NOTE: Use GetKeyPressed() to allow redefining required keys for alternative layouts typedef enum { KEY_NULL = 0, // Key: NULL, used for no key pressed // Alphanumeric keys From b365d23f49d352019eb29f2f4e52d481c48c70dd Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 9 Jan 2026 19:59:38 +0100 Subject: [PATCH 090/117] Update examples_list.txt --- examples/examples_list.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/examples_list.txt b/examples/examples_list.txt index 96d64ca84..ba3a16d08 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -124,6 +124,7 @@ textures;textures_screen_buffer;★★☆☆;5.5;5.5;2025;2025;"Agnis Aldiņš"; textures;textures_textured_curve;★★★☆;4.5;4.5;2022;2025;"Jeffery Myers";@JeffM2501 textures;textures_sprite_stacking;★★☆☆;5.6-dev;6.0;2025;2025;"Robin";@RobinsAviary textures;textures_cellular_automata;★★☆☆;5.6;5.6;2025;2025;"Jordi Santonja";@JordSant +texture;textures_framebuffer_rendering;★★☆☆;5.6;5.6;2026;2026;"Jack Boakes";@jackboakes text;text_sprite_fonts;★☆☆☆;1.7;3.7;2017;2025;"Ramon Santamaria";@raysan5 text;text_font_spritefont;★☆☆☆;1.0;1.0;2014;2025;"Ramon Santamaria";@raysan5 text;text_font_filters;★★☆☆;1.3;4.2;2015;2025;"Ramon Santamaria";@raysan5 From cfd5c3f2abe2a54cfa5cc60806601455feafc769 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 9 Jan 2026 20:02:29 +0100 Subject: [PATCH 091/117] Update examples_list.txt --- examples/examples_list.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/examples_list.txt b/examples/examples_list.txt index ba3a16d08..c825e137d 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -124,7 +124,7 @@ textures;textures_screen_buffer;★★☆☆;5.5;5.5;2025;2025;"Agnis Aldiņš"; textures;textures_textured_curve;★★★☆;4.5;4.5;2022;2025;"Jeffery Myers";@JeffM2501 textures;textures_sprite_stacking;★★☆☆;5.6-dev;6.0;2025;2025;"Robin";@RobinsAviary textures;textures_cellular_automata;★★☆☆;5.6;5.6;2025;2025;"Jordi Santonja";@JordSant -texture;textures_framebuffer_rendering;★★☆☆;5.6;5.6;2026;2026;"Jack Boakes";@jackboakes +textures;textures_framebuffer_rendering;★★☆☆;5.6;5.6;2026;2026;"Jack Boakes";@jackboakes text;text_sprite_fonts;★☆☆☆;1.7;3.7;2017;2025;"Ramon Santamaria";@raysan5 text;text_font_spritefont;★☆☆☆;1.0;1.0;2014;2025;"Ramon Santamaria";@raysan5 text;text_font_filters;★★☆☆;1.3;4.2;2015;2025;"Ramon Santamaria";@raysan5 From 7218b674e5cab56a89ffad8e4f9ec5cea1e1c06e Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 9 Jan 2026 20:05:46 +0100 Subject: [PATCH 092/117] REXM: Updated: `textures_framebuffer_rendering` --- examples/Makefile | 1 + examples/Makefile.Web | 4 + .../textures_framebuffer_rendering.vcxproj | 569 ++++++++++++++++++ tools/rexm/reports/examples_issues.md | 1 + tools/rexm/reports/examples_validation.md | 3 +- 5 files changed, 577 insertions(+), 1 deletion(-) create mode 100644 projects/VS2022/examples/textures_framebuffer_rendering.vcxproj diff --git a/examples/Makefile b/examples/Makefile index 3cbc2ffc1..a402ae692 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -608,6 +608,7 @@ TEXTURES = \ textures/textures_bunnymark \ textures/textures_cellular_automata \ textures/textures_fog_of_war \ + textures/textures_framebuffer_rendering \ textures/textures_gif_player \ textures/textures_image_channel \ textures/textures_image_drawing \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index fe4de1330..556900a76 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -594,6 +594,7 @@ TEXTURES = \ textures/textures_bunnymark \ textures/textures_cellular_automata \ textures/textures_fog_of_war \ + textures/textures_framebuffer_rendering \ textures/textures_gif_player \ textures/textures_image_channel \ textures/textures_image_drawing \ @@ -1009,6 +1010,9 @@ textures/textures_cellular_automata: textures/textures_cellular_automata.c textures/textures_fog_of_war: textures/textures_fog_of_war.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +textures/textures_framebuffer_rendering: textures/textures_framebuffer_rendering.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + textures/textures_gif_player: textures/textures_gif_player.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file textures/resources/scarfy_run.gif@resources/scarfy_run.gif diff --git a/projects/VS2022/examples/textures_framebuffer_rendering.vcxproj b/projects/VS2022/examples/textures_framebuffer_rendering.vcxproj new file mode 100644 index 000000000..3a7eeeb35 --- /dev/null +++ b/projects/VS2022/examples/textures_framebuffer_rendering.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E} + Win32Proj + textures_framebuffer_rendering + 10.0 + textures_framebuffer_rendering + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\textures + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/tools/rexm/reports/examples_issues.md b/tools/rexm/reports/examples_issues.md index 081170806..7b39c7a0b 100644 --- a/tools/rexm/reports/examples_issues.md +++ b/tools/rexm/reports/examples_issues.md @@ -21,6 +21,7 @@ Example elements validated: | **EXAMPLE NAME** | [C] | [CAT]| [INFO]|[PNG]|[WPNG]| [RES]| [MK] |[MKWEB]| [VCX]| [SOL]|[RDME]|[JS] | [WOUT]|[WMETA]| |:---------------------------------|:---:|:----:|:-----:|:---:|:----:|:----:|:----:|:-----:|:----:|:----:|:----:|:---:|:-----:|:-----:| | core_highdpi_testbed | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_keyboard_testbed | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | rlgl_standalone | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | rlgl_compute_shader | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | easings_testbed | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | diff --git a/tools/rexm/reports/examples_validation.md b/tools/rexm/reports/examples_validation.md index 6770f3c37..8d3f699ca 100644 --- a/tools/rexm/reports/examples_validation.md +++ b/tools/rexm/reports/examples_validation.md @@ -105,6 +105,7 @@ Example elements validated: | shapes_rlgl_triangle | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_ball_physics | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_penrose_tile | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| shapes_hilbert_curve | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_logo_raylib | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_srcrec_dstrec | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_image_drawing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -134,6 +135,7 @@ Example elements validated: | textures_textured_curve | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_sprite_stacking | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | textures_cellular_automata | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| textures_framebuffer_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | text_sprite_fonts | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | text_font_spritefont | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | text_font_filters | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -225,4 +227,3 @@ Example elements validated: | raylib_opengl_interop | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | | embedded_files_loading | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | web_basic_window | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| shapes_hilbert_curve | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | From 11f7db2dd8c00b397980fab82372ef17c4b9a709 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 9 Jan 2026 20:06:53 +0100 Subject: [PATCH 093/117] REXM: ADDED: `core_keyboard_testbed` --- examples/Makefile | 1 + examples/Makefile.Web | 4 + examples/README.md | 8 +- examples/core/core_keyboard_testbed.c | 333 ++++++++++ examples/core/core_keyboard_testbed.png | Bin 0 -> 17631 bytes examples/examples_list.txt | 1 + .../examples/core_keyboard_testbed.vcxproj | 569 ++++++++++++++++++ projects/VS2022/raylib.sln | 54 ++ tools/rexm/reports/examples_issues.md | 1 - tools/rexm/reports/examples_validation.md | 1 + 10 files changed, 968 insertions(+), 4 deletions(-) create mode 100644 examples/core/core_keyboard_testbed.c create mode 100644 examples/core/core_keyboard_testbed.png create mode 100644 projects/VS2022/examples/core_keyboard_testbed.vcxproj diff --git a/examples/Makefile b/examples/Makefile index a402ae692..acfcb0857 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -544,6 +544,7 @@ CORE = \ core/core_input_mouse_wheel \ core/core_input_multitouch \ core/core_input_virtual_controls \ + core/core_keyboard_testbed \ core/core_monitor_detector \ core/core_random_sequence \ core/core_random_values \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 556900a76..3841dff29 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -530,6 +530,7 @@ CORE = \ core/core_input_mouse_wheel \ core/core_input_multitouch \ core/core_input_virtual_controls \ + core/core_keyboard_testbed \ core/core_monitor_detector \ core/core_random_sequence \ core/core_random_values \ @@ -820,6 +821,9 @@ core/core_input_multitouch: core/core_input_multitouch.c core/core_input_virtual_controls: core/core_input_virtual_controls.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +core/core_keyboard_testbed: core/core_keyboard_testbed.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + core/core_monitor_detector: core/core_monitor_detector.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) diff --git a/examples/README.md b/examples/README.md index d9b03669d..6b2c1950b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,9 +17,9 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES COLLECTION [TOTAL: 206] +## EXAMPLES COLLECTION [TOTAL: 208] -### category: core [47] +### category: core [48] Examples using raylib [core](../src/rcore.c) module platform functionality: window creation, inputs, drawing modes and system functionality. @@ -72,6 +72,7 @@ Examples using raylib [core](../src/rcore.c) module platform functionality: wind | [core_clipboard_text](core/core_clipboard_text.c) | core_clipboard_text | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Ananth S](https://github.com/Ananth1839) | | [core_text_file_loading](core/core_text_file_loading.c) | core_text_file_loading | ⭐☆☆☆ | 5.5 | 5.6 | [Aanjishnu Bhattacharyya](https://github.com/NimComPoo-04) | | [core_compute_hash](core/core_compute_hash.c) | core_compute_hash | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) | +| [core_keyboard_testbed](core/core_keyboard_testbed.c) | core_keyboard_testbed | ⭐⭐☆☆ | 5.6 | 5.6 | [Ramon Santamaria](https://github.com/raysan5) | ### category: shapes [39] @@ -119,7 +120,7 @@ Examples using raylib shapes drawing functionality, provided by raylib [shapes]( | [shapes_penrose_tile](shapes/shapes_penrose_tile.c) | shapes_penrose_tile | ⭐⭐⭐⭐️ | 5.5 | 5.6-dev | [David Buzatto](https://github.com/davidbuzatto) | | [shapes_hilbert_curve](shapes/shapes_hilbert_curve.c) | shapes_hilbert_curve | ⭐⭐⭐☆ | 5.6 | 5.6 | [Hamza RAHAL](https://github.com/hmz-rhl) | -### category: textures [29] +### category: textures [30] Examples using raylib textures functionality, including image/textures loading/generation and drawing, provided by raylib [textures](../src/rtextures.c) module. @@ -154,6 +155,7 @@ Examples using raylib textures functionality, including image/textures loading/g | [textures_textured_curve](textures/textures_textured_curve.c) | textures_textured_curve | ⭐⭐⭐☆ | 4.5 | 4.5 | [Jeffery Myers](https://github.com/JeffM2501) | | [textures_sprite_stacking](textures/textures_sprite_stacking.c) | textures_sprite_stacking | ⭐⭐☆☆ | 5.6-dev | 6.0 | [Robin](https://github.com/RobinsAviary) | | [textures_cellular_automata](textures/textures_cellular_automata.c) | textures_cellular_automata | ⭐⭐☆☆ | 5.6 | 5.6 | [Jordi Santonja](https://github.com/JordSant) | +| [textures_framebuffer_rendering](textures/textures_framebuffer_rendering.c) | textures_framebuffer_rendering | ⭐⭐☆☆ | 5.6 | 5.6 | [Jack Boakes](https://github.com/jackboakes) | ### category: text [16] diff --git a/examples/core/core_keyboard_testbed.c b/examples/core/core_keyboard_testbed.c new file mode 100644 index 000000000..904e9c90a --- /dev/null +++ b/examples/core/core_keyboard_testbed.c @@ -0,0 +1,333 @@ +/******************************************************************************************* +* +* raylib [core] example - keyboard testbed +* +* Example complexity rating: [★★☆☆] 2/4 +* +* NOTE: raylib defined keys refer to ENG-US Keyboard layout, +* mapping to other layouts is up to the user +* +* Example originally created with raylib 5.6, last time updated with raylib 5.6 +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2026 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#define KEY_REC_SPACING 4 // Space in pixels between key rectangles + +//------------------------------------------------------------------------------------ +// Module Functions Declaration +//------------------------------------------------------------------------------------ +static const char *GetKeyText(int key); +static void GuiKeyboardKey(Rectangle bounds, int key); + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - keyboard testbed"); + SetExitKey(KEY_NULL); // Avoid exit on KEY_ESCAPE + + // Keyboard line 01 + int line01KeyWidths[15] = { 0 }; + for (int i = 0; i < 15; i++) line01KeyWidths[i] = 45; + line01KeyWidths[13] = 62; // PRINTSCREEN + int line01Keys[15] = { + KEY_ESCAPE, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, + KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, + KEY_F12, KEY_PRINT_SCREEN, KEY_PAUSE + }; + + // Keyboard line 02 + int line02KeyWidths[15] = { 0 }; + for (int i = 0; i < 15; i++) line02KeyWidths[i] = 45; + line02KeyWidths[0] = 25; // GRAVE + line02KeyWidths[13] = 82; // BACKSPACE + int line02Keys[15] = { + KEY_GRAVE, KEY_ONE, KEY_TWO, KEY_THREE, KEY_FOUR, + KEY_FIVE, KEY_SIX, KEY_SEVEN, KEY_EIGHT, KEY_NINE, + KEY_ZERO, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_DELETE }; + + // Keyboard line 03 + int line03KeyWidths[15] = { 0 }; + for (int i = 0; i < 15; i++) line03KeyWidths[i] = 45; + line03KeyWidths[0] = 50; // TAB + line03KeyWidths[13] = 57; // BACKSLASH + int line03Keys[15] = { + KEY_TAB, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, + KEY_U, KEY_I, KEY_O, KEY_P, KEY_LEFT_BRACKET, + KEY_RIGHT_BRACKET, KEY_BACKSLASH, KEY_INSERT + }; + + // Keyboard line 04 + int line04KeyWidths[14] = { 0 }; + for (int i = 0; i < 14; i++) line04KeyWidths[i] = 45; + line04KeyWidths[0] = 68; // CAPS + line04KeyWidths[12] = 88; // ENTER + int line04Keys[14] = { + KEY_CAPS_LOCK, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, + KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON, + KEY_APOSTROPHE, KEY_ENTER, KEY_PAGE_UP + }; + + // Keyboard line 05 + int line05KeyWidths[14] = { 0 }; + for (int i = 0; i < 14; i++) line05KeyWidths[i] = 45; + line05KeyWidths[0] = 80; // LSHIFT + line05KeyWidths[11] = 76; // RSHIFT + int line05Keys[14] = { + KEY_LEFT_SHIFT, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, + KEY_N, KEY_M, KEY_COMMA, KEY_PERIOD, /*KEY_MINUS*/ + KEY_SLASH, KEY_RIGHT_SHIFT, KEY_UP, KEY_PAGE_DOWN + }; + + // Keyboard line 06 + int line06KeyWidths[11] = { 0 }; + for (int i = 0; i < 11; i++) line06KeyWidths[i] = 45; + line06KeyWidths[0] = 80; // LCTRL + line06KeyWidths[3] = 208; // SPACE + line06KeyWidths[7] = 60; // RCTRL + int line06Keys[11] = { + KEY_LEFT_CONTROL, KEY_LEFT_SUPER, KEY_LEFT_ALT, + KEY_SPACE, KEY_RIGHT_ALT, 162, KEY_NULL, + KEY_RIGHT_CONTROL, KEY_LEFT, KEY_DOWN, KEY_RIGHT + }; + + Vector2 keyboardOffset = { 26, 80 }; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + int key = GetKeyPressed(); // Get pressed keycode + if (key > 0) TraceLog(LOG_INFO, "KEYBOARD TESTBED: KEY PRESSED: %d", key); + + int ch = GetCharPressed(); // Get pressed char for text input, using OS mapping + if (ch > 0) TraceLog(LOG_INFO, "KEYBOARD TESTBED: CHAR PRESSED: %c (%d)", ch, ch); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("KEYBOARD LAYOUT: ENG-US", 26, 38, 20, LIGHTGRAY); + + // Keyboard line 01 - 15 keys + // ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, IMP, CLOSE + for (int i = 0, recOffsetX = 0; i < 15; i++) + { + GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y, line01KeyWidths[i], 30 }, line01Keys[i]); + recOffsetX += line01KeyWidths[i] + KEY_REC_SPACING; + } + + // Keyboard line 02 - 15 keys + // `, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, -, =, BACKSPACE, DEL + for (int i = 0, recOffsetX = 0; i < 15; i++) + { + GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + KEY_REC_SPACING, line02KeyWidths[i], 38 }, line02Keys[i]); + recOffsetX += line02KeyWidths[i] + KEY_REC_SPACING; + } + + // Keyboard line 03 - 15 keys + // TAB, Q, W, E, R, T, Y, U, I, O, P, [, ], \, INS + for (int i = 0, recOffsetX = 0; i < 15; i++) + { + GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38 + KEY_REC_SPACING*2, line03KeyWidths[i], 38 }, line03Keys[i]); + recOffsetX += line03KeyWidths[i] + KEY_REC_SPACING; + } + + // Keyboard line 04 - 14 keys + // MAYUS, A, S, D, F, G, H, J, K, L, ;, ', ENTER, REPAG + for (int i = 0, recOffsetX = 0; i < 14; i++) + { + GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*2 + KEY_REC_SPACING*3, line04KeyWidths[i], 38 }, line04Keys[i]); + recOffsetX += line04KeyWidths[i] + KEY_REC_SPACING; + } + + // Keyboard line 05 - 14 keys + // LSHIFT, Z, X, C, V, B, N, M, ,, ., /, RSHIFT, UP, AVPAG + for (int i = 0, recOffsetX = 0; i < 14; i++) + { + GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*3 + KEY_REC_SPACING*4, line05KeyWidths[i], 38 }, line05Keys[i]); + recOffsetX += line05KeyWidths[i] + KEY_REC_SPACING; + } + + // Keyboard line 06 - 11 keys + // LCTRL, WIN, LALT, SPACE, ALTGR, \, FN, RCTRL, LEFT, DOWN, RIGHT + for (int i = 0, recOffsetX = 0; i < 11; i++) + { + GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*4 + KEY_REC_SPACING*5, line06KeyWidths[i], 38 }, line06Keys[i]); + recOffsetX += line06KeyWidths[i] + KEY_REC_SPACING; + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + +//------------------------------------------------------------------------------------ +// Module Functions Definition +//------------------------------------------------------------------------------------ +// Get keyboard keycode as text (US keyboard) +// NOTE: Mapping for other keyboard layouts can be done here +static const char *GetKeyText(int key) +{ + switch (key) + { + case KEY_APOSTROPHE : return "'"; // Key: ' + case KEY_COMMA : return ","; // Key: , + case KEY_MINUS : return "-"; // Key: - + case KEY_PERIOD : return "."; // Key: . + case KEY_SLASH : return "/"; // Key: / + case KEY_ZERO : return "0"; // Key: 0 + case KEY_ONE : return "1"; // Key: 1 + case KEY_TWO : return "2"; // Key: 2 + case KEY_THREE : return "3"; // Key: 3 + case KEY_FOUR : return "4"; // Key: 4 + case KEY_FIVE : return "5"; // Key: 5 + case KEY_SIX : return "6"; // Key: 6 + case KEY_SEVEN : return "7"; // Key: 7 + case KEY_EIGHT : return "8"; // Key: 8 + case KEY_NINE : return "9"; // Key: 9 + case KEY_SEMICOLON : return ";"; // Key: ; + case KEY_EQUAL : return "="; // Key: = + case KEY_A : return "A"; // Key: A | a + case KEY_B : return "B"; // Key: B | b + case KEY_C : return "C"; // Key: C | c + case KEY_D : return "D"; // Key: D | d + case KEY_E : return "E"; // Key: E | e + case KEY_F : return "F"; // Key: F | f + case KEY_G : return "G"; // Key: G | g + case KEY_H : return "H"; // Key: H | h + case KEY_I : return "I"; // Key: I | i + case KEY_J : return "J"; // Key: J | j + case KEY_K : return "K"; // Key: K | k + case KEY_L : return "L"; // Key: L | l + case KEY_M : return "M"; // Key: M | m + case KEY_N : return "N"; // Key: N | n + case KEY_O : return "O"; // Key: O | o + case KEY_P : return "P"; // Key: P | p + case KEY_Q : return "Q"; // Key: Q | q + case KEY_R : return "R"; // Key: R | r + case KEY_S : return "S"; // Key: S | s + case KEY_T : return "T"; // Key: T | t + case KEY_U : return "U"; // Key: U | u + case KEY_V : return "V"; // Key: V | v + case KEY_W : return "W"; // Key: W | w + case KEY_X : return "X"; // Key: X | x + case KEY_Y : return "Y"; // Key: Y | y + case KEY_Z : return "Z"; // Key: Z | z + case KEY_LEFT_BRACKET : return "["; // Key: [ + case KEY_BACKSLASH : return "\\"; // Key: '\' + case KEY_RIGHT_BRACKET : return "]"; // Key: ] + case KEY_GRAVE : return "`"; // Key: ` + case KEY_SPACE : return "SPACE"; // Key: Space + case KEY_ESCAPE : return "ESC"; // Key: Esc + case KEY_ENTER : return "ENTER"; // Key: Enter + case KEY_TAB : return "TAB"; // Key: Tab + case KEY_BACKSPACE : return "BACK"; // Key: Backspace + case KEY_INSERT : return "INS"; // Key: Ins + case KEY_DELETE : return "DEL"; // Key: Del + case KEY_RIGHT : return "RIGHT"; // Key: Cursor right + case KEY_LEFT : return "LEFT"; // Key: Cursor left + case KEY_DOWN : return "DOWN"; // Key: Cursor down + case KEY_UP : return "UP"; // Key: Cursor up + case KEY_PAGE_UP : return "PGUP"; // Key: Page up + case KEY_PAGE_DOWN : return "PGDOWN"; // Key: Page down + case KEY_HOME : return "HOME"; // Key: Home + case KEY_END : return "END"; // Key: End + case KEY_CAPS_LOCK : return "CAPS"; // Key: Caps lock + case KEY_SCROLL_LOCK : return "LOCK"; // Key: Scroll down + case KEY_NUM_LOCK : return "NUMLOCK"; // Key: Num lock + case KEY_PRINT_SCREEN : return "PRINTSCR"; // Key: Print screen + case KEY_PAUSE : return "PAUSE"; // Key: Pause + case KEY_F1 : return "F1"; // Key: F1 + case KEY_F2 : return "F2"; // Key: F2 + case KEY_F3 : return "F3"; // Key: F3 + case KEY_F4 : return "F4"; // Key: F4 + case KEY_F5 : return "F5"; // Key: F5 + case KEY_F6 : return "F6"; // Key: F6 + case KEY_F7 : return "F7"; // Key: F7 + case KEY_F8 : return "F8"; // Key: F8 + case KEY_F9 : return "F9"; // Key: F9 + case KEY_F10 : return "F10"; // Key: F10 + case KEY_F11 : return "F11"; // Key: F11 + case KEY_F12 : return "F12"; // Key: F12 + case KEY_LEFT_SHIFT : return "LSHIFT"; // Key: Shift left + case KEY_LEFT_CONTROL : return "LCTRL"; // Key: Control left + case KEY_LEFT_ALT : return "LALT"; // Key: Alt left + case KEY_LEFT_SUPER : return "WIN"; // Key: Super left + case KEY_RIGHT_SHIFT : return "RSHIFT"; // Key: Shift right + case KEY_RIGHT_CONTROL : return "RCTRL"; // Key: Control right + case KEY_RIGHT_ALT : return "ALTGR"; // Key: Alt right + case KEY_RIGHT_SUPER : return "RSUPER"; // Key: Super right + case KEY_KB_MENU : return "KBMENU"; // Key: KB menu + case KEY_KP_0 : return "KP0"; // Key: Keypad 0 + case KEY_KP_1 : return "KP1"; // Key: Keypad 1 + case KEY_KP_2 : return "KP2"; // Key: Keypad 2 + case KEY_KP_3 : return "KP3"; // Key: Keypad 3 + case KEY_KP_4 : return "KP4"; // Key: Keypad 4 + case KEY_KP_5 : return "KP5"; // Key: Keypad 5 + case KEY_KP_6 : return "KP6"; // Key: Keypad 6 + case KEY_KP_7 : return "KP7"; // Key: Keypad 7 + case KEY_KP_8 : return "KP8"; // Key: Keypad 8 + case KEY_KP_9 : return "KP9"; // Key: Keypad 9 + case KEY_KP_DECIMAL : return "KPDEC"; // Key: Keypad . + case KEY_KP_DIVIDE : return "KPDIV"; // Key: Keypad / + case KEY_KP_MULTIPLY : return "KPMUL"; // Key: Keypad * + case KEY_KP_SUBTRACT : return "KPSUB"; // Key: Keypad - + case KEY_KP_ADD : return "KPADD"; // Key: Keypad + + case KEY_KP_ENTER : return "KPENTER"; // Key: Keypad Enter + case KEY_KP_EQUAL : return "KPEQU"; // Key: Keypad = + default: return ""; + } +} + +// Draw keyboard key +static void GuiKeyboardKey(Rectangle bounds, int key) +{ + if (key == KEY_NULL) DrawRectangleLinesEx(bounds, 2.0f, LIGHTGRAY); + else + { + if (IsKeyDown(key)) + { + DrawRectangleLinesEx(bounds, 2.0f, MAROON); + DrawText(GetKeyText(key), bounds.x + 4, bounds.y + 4, 10, MAROON); + } + else + { + DrawRectangleLinesEx(bounds, 2.0f, DARKGRAY); + DrawText(GetKeyText(key), bounds.x + 4, bounds.y + 4, 10, DARKGRAY); + } + } + + if (CheckCollisionPointRec(GetMousePosition(), bounds)) + { + DrawRectangleRec(bounds, Fade(RED, 0.2f)); + DrawRectangleLinesEx(bounds, 3.0f, RED); + } +} \ No newline at end of file diff --git a/examples/core/core_keyboard_testbed.png b/examples/core/core_keyboard_testbed.png new file mode 100644 index 0000000000000000000000000000000000000000..bac0fc29ac41ee31592622b54bf65af602427995 GIT binary patch literal 17631 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 literal 0 HcmV?d00001 diff --git a/examples/examples_list.txt b/examples/examples_list.txt index c825e137d..eda62ee13 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -56,6 +56,7 @@ core;core_screen_recording;★★☆☆;5.6-dev;5.6-dev;2025;2025;"Ramon Santama core;core_clipboard_text;★★☆☆;5.6-dev;5.6-dev;2025;2025;"Ananth S";@Ananth1839 core;core_text_file_loading;★☆☆☆;5.5;5.6;0;0;"Aanjishnu Bhattacharyya";@NimComPoo-04 core;core_compute_hash;★★☆☆;5.6-dev;5.6-dev;2025;2025;"Ramon Santamaria";@raysan5 +core;core_keyboard_testbed;★★☆☆;5.6;5.6;2026;2026;"Ramon Santamaria";@raysan5 shapes;shapes_basic_shapes;★☆☆☆;1.0;4.2;2014;2025;"Ramon Santamaria";@raysan5 shapes;shapes_bouncing_ball;★☆☆☆;2.5;2.5;2013;2025;"Ramon Santamaria";@raysan5 shapes;shapes_bullet_hell;★☆☆☆;5.6;5.6;2025;2025;"Zero";@zerohorsepower diff --git a/projects/VS2022/examples/core_keyboard_testbed.vcxproj b/projects/VS2022/examples/core_keyboard_testbed.vcxproj new file mode 100644 index 000000000..3a146b762 --- /dev/null +++ b/projects/VS2022/examples/core_keyboard_testbed.vcxproj @@ -0,0 +1,569 @@ + + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {6B1A933E-71B8-4C1F-9E79-02D98830E671} + Win32Proj + core_keyboard_testbed + 10.0 + core_keyboard_testbed + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index df2633843..93c4efaf1 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -433,6 +433,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_cellular_automata" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_hilbert_curve", "examples\shapes_hilbert_curve.vcxproj", "{DC163251-16C3-4B72-B965-ACDBA0F02BD1}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_keyboard_testbed", "examples\core_keyboard_testbed.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_framebuffer_rendering", "examples\textures_framebuffer_rendering.vcxproj", "{2CCCD9E4-9058-4291-BD89-39C979F0CA1E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|ARM64 = Debug.DLL|ARM64 @@ -5391,6 +5395,54 @@ Global {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x64.Build.0 = Release|x64 {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x86.ActiveCfg = Release|Win32 {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x86.Build.0 = Release|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.Build.0 = Debug|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.ActiveCfg = Debug|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.Build.0 = Debug|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.ActiveCfg = Debug|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.Build.0 = Debug|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.ActiveCfg = Release|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.Build.0 = Release|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.ActiveCfg = Release|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|ARM64.Build.0 = Debug|ARM64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|x64.ActiveCfg = Debug|x64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|x64.Build.0 = Debug|x64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|x86.ActiveCfg = Debug|Win32 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|x86.Build.0 = Debug|Win32 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|ARM64.ActiveCfg = Release|ARM64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|ARM64.Build.0 = Release|ARM64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|x64.ActiveCfg = Release|x64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|x64.Build.0 = Release|x64 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|x86.ActiveCfg = Release|Win32 + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -5609,6 +5661,8 @@ Global {1F4722E7-F78E-413F-A106-D3490211EA57} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {0A0FC982-6E31-401F-BA77-3C5E8AB02C68} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {DC163251-16C3-4B72-B965-ACDBA0F02BD1} = {278D8859-20B1-428F-8448-064F46E1F021} + {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} diff --git a/tools/rexm/reports/examples_issues.md b/tools/rexm/reports/examples_issues.md index 7b39c7a0b..081170806 100644 --- a/tools/rexm/reports/examples_issues.md +++ b/tools/rexm/reports/examples_issues.md @@ -21,7 +21,6 @@ Example elements validated: | **EXAMPLE NAME** | [C] | [CAT]| [INFO]|[PNG]|[WPNG]| [RES]| [MK] |[MKWEB]| [VCX]| [SOL]|[RDME]|[JS] | [WOUT]|[WMETA]| |:---------------------------------|:---:|:----:|:-----:|:---:|:----:|:----:|:----:|:-----:|:----:|:----:|:----:|:---:|:-----:|:-----:| | core_highdpi_testbed | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| core_keyboard_testbed | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | rlgl_standalone | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | rlgl_compute_shader | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | easings_testbed | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | diff --git a/tools/rexm/reports/examples_validation.md b/tools/rexm/reports/examples_validation.md index 8d3f699ca..d8f4a9521 100644 --- a/tools/rexm/reports/examples_validation.md +++ b/tools/rexm/reports/examples_validation.md @@ -67,6 +67,7 @@ Example elements validated: | core_clipboard_text | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_text_file_loading | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_compute_hash | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_keyboard_testbed | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_basic_shapes | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_bouncing_ball | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_bullet_hell | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | From a6dd2af9e993a3a1d22696324e01f5b4f446c890 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 9 Jan 2026 20:06:57 +0100 Subject: [PATCH 094/117] Update rexm.c --- tools/rexm/rexm.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 9914733f0..95ed13739 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -570,7 +570,7 @@ int main(int argc, char *argv[]) // ----------------------------------------------------------------------------------------- // Add example to the collection list, if not already there - // NOTE: Required format: shapes;shapes_basic_shapes;★☆☆☆;1.0;4.2;2014;2025;"Ray";@raysan5 + // NOTE: Required format: shapes;shapes_basic_shapes;★☆☆☆;1.0;4.2;2014;2026;"Ray";@raysan5 //------------------------------------------------------------------------------------------------ char *exCollectionList = LoadFileText(exCollectionFilePath); if (TextFindIndex(exCollectionList, exName) == -1) // Example not found @@ -2440,7 +2440,7 @@ static void UnloadExampleInfo(rlExampleInfo *exInfo) } // raylib example line info parser -// Parses following line format: core;core_basic_window;★☆☆☆;1.0;1.0;2013;2025;"Ray";@raysan5 +// Parses following line format: core;core_basic_window;★☆☆☆;1.0;1.0;2013;2026;"Ray";@raysan5 static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry) { #define MAX_EXAMPLE_INFO_LINE_LEN 512 @@ -2452,7 +2452,10 @@ static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry) int tokenCount = 0; char **tokens = TextSplit(line, ';', &tokenCount); - if (tokenCount != 9) LOG("REXM: WARNING: Example collection line contains invalid number of tokens: %i\n", tokenCount); + if (tokenCount != 9) + { + LOG("REXM: WARNING: Example collection line contains invalid number of tokens: %i\n", tokenCount); + } // Get category and name strcpy(entry->category, tokens[0]); From 1284d687213ae53313cf69f283e04a0e6c200c17 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 9 Jan 2026 20:18:45 +0100 Subject: [PATCH 095/117] Update core_highdpi_testbed.png --- examples/core/core_highdpi_testbed.png | Bin 17323 -> 18560 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/examples/core/core_highdpi_testbed.png b/examples/core/core_highdpi_testbed.png index da99bbb0d97118eef3ca41c16b62182acced63f9..a37c821304b22cc7cbcb463de52eb95d0b5c2a90 100644 GIT binary patch literal 18560 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 literal 17323 zcmai6c|cUv{+|KF5eFH=bl5?R=aPwVMFqrx!5QtnDH2=cmpxR%i`&Bh{P7(^qao3Oe9i%G9f&4Qqo7$7w5-4+ak7WJM-ss zc{ih%ud|iqABu8W8(V8LGV|`OUw1#x>89{Ay5(>-BVRf`P7-}?H_S6 zxqXV|AD*w-#A`m$&QN6AJ8!mk(tKbU0Fc$`M0w9_RNZZ2IaVFRa2dK{R-(7?L*`<2 zwACX%QK1UzZ(KEpu~8|bE&R|%NU{Uxh3>Jo+83Xoiec`)bi#;>W25^ANVvCucK*E} z#L#AWzUUa8=(V+u;b*j--5cv?m$C1;Sl)C_KdY+x?%C(WDcxGgPA>T=zud^W<+@{&!| z>qI**-JjH?Enc2n=M$GIna5sn zJiL5bg7MKyw@}jC%KC>X%jf!gmW7TvI5vj4Wm#^br^;yG!qV6Y<5B z#nCetII!Jg$VPv+yiskUm#@70lh19HX<4@&oXQg|+o$4pnf%*tGHj1YC7(oOy>ZI9 zJozO9mrA6qtRr`q=MZ;ytcb$~o0bD;Ds!eqpW!Gl%K> zDx(W_$)`CF&KZ61oh2``H_-qa-Mz#WUjEk0zxYk{V5L6xu%$(Fl3D3c8{I9> z$9p!4L*JTh+~;LnamJD>MD$9jx$>o3qMPi4X)X|1-(X9VDF@l8-h4d&H%boOPFtKw zl9Ce;AMz)57(O!4+b>FF$r$qMCn};rSmKv}Zf~}F*8Jrb^eCRW zvY7qDdQp1jX>IO`Q*4;i(8ZP%Ij~0x*E--?g-fHhTC#;p^N+e?Y1Qmktc;E7+OS!L z)?uDss2tC-CH@wIgLasW>e8oMw`Fm?Bsn!B40}{PzqACdP9~q@v1Wt2YGj_H(_Ev= zId#u3ERFj>qE8Gdd%LI6-!mrdcXqjprA@>jMg(QwOY&&cM#yx=9`S|sZZ8;PPH$`= zQAq{xZ;;yz86m$mmbQzEzS$um8$7Z_+A zc0JxrQ(!#S^n#&D8?nbTT);C`kSAkzl}ihXYR-HinpN0A{4PssiCnM6yKBmf<19>d z8&mD(MlQuv33g3f+89eyRgn~2o*jW<*?6tP zATo7cJZM&eLz!p#TJEWraop_}#1%0XX6=#c|8B-s#+W1(h8nU^IA=k(W zoSM&fsR3v5q0HDS&W`OePXXv*QEbxJHIR{S##K%q6!ox+<4gBLLO<#COb{e=<8H7a zM?GXqm!(SFp^)`C>s9I?PwxRgs{LEp>s^WAH<-y1KflVldY|UruQU;Fe*tny9|AdN zlRF@*qAzE~w!H{B7xTFJ8Cs!x?@{*pz9M6{E*YZeFtNzthV#*|IkA(br_|W(2;09) za+>=t#O$xC?3*mn-^M6MDUqUMNoSiB4aHpdhZ}b7Sy5asEn^QVz?93UF`-?g6anXH zT*WbIpg!b54c4;;Q5;fYvOVO-VO_}-G}&p$S9-yB7S`_DdPD#noBX{t6% zHD;{g;C44F<06Hsvd=LuUy{uA?i6G1NK?4e$Jvwl<0SgiT7CYI9Yh)<47r`=s>cFL zLJX_poTx0ut(D1#7O5Q1chr>huuQ)5Cw1h|F;uXpS!|-)1=gxlrd;A-Wo&6-2XC7J z`U5PNDHt8?qG3BF5pDQ#sk>&qHT-!~g}TxWqe#jc)_Ja1iLG*dMn`!0211ps^>Bi2 zSJ?&CFk&2Ua3NZOq1;#8l^ZH0cPy0{H&AnH2o*Ui|?6Hv3hFP<{e4 zIb`NP&x5>)nFhz;A(6Z0kXeb|$p3B~=9)u3q}K4+%Ed6{GUKWw^J1f5R%1pQeq8x~ z)c-4$AvPqB@~Jg%t_C}Rjtj@QDx(u@W0)xp-@x_?)Q-(&+l$9O9?fRl9R85W8)3vv zQ(>pw9^E&a0!8HDC8^Za6tak*kg$q zvM+S>lrg-ao+i=H4fD`E@P?$3mHITBA(tRF0x~mtB8A+TRRqX))Q(ThkQKx`8N)Lb zs1I&%zc?LG?V_2xS@;7jwk!MrXEWCv;B1vm_m$Qg&hzRK*2m1FxgWzK{f!N?7#k=K zft_6~ze{b>(aet(gL(3a!zh#}M2zq1b)tz9{d;|kC#JUVPU{RIZPDi&Iax7S1DdTG z?rik|q){u^^fe2r4q5cm;fA>?r8b_2A9;e6RD+dQ$1n@ctICkOv>amto$ST65H=h% z3QwfNpg&Np#S^BHq67HGLsfZyT=z9|VXOE48mNGU40EvyRW1Ow} zK=dmZLz+N+NifgRV+}{tO6@_2w0jIQE<-0;L8aS6pwT-}92oSbyS@14NVpxN@Q0J16I0dj$vGXF;+H)pZNXua-U{EAw74QU*!uHvW4GVXD z^#Xg(*<{Rlkj(WK_Dwf?@gp!9VC@GiH)v^^izZgk(kMuLvAgDTtn!>jrkfCcFMvm3 z`ndqU8r%r*J3ve+(4P|of!;v^WN7yv=7cb+7@HX#Y_I|`MN>tRU$t1#%2gwj--@dM zzd#*%nUzR0sl6VBPgG-zGWFVO!KedlRHrU*ePGn7L*VT2)-pQkcMm^&k)8~xIZ9U%ru2bAPKW8iDKd9)ziaG`C=3C>51e1&GfkiwVW$=Sm>k$Fq z^plYEDm^I$RGQ{0rZIH~yfhC)L<|)+B+f^I7o~ThafmaG z7lH1-)NdZ(4?of5;~XTWhw#2~*PP}JrWED#seN2wu(MU^NV@$+fIJnDFB?xJ^NyDXpkJmeA(`t?fo zB1{hbIo{m~FKPi9Ex%uQ$RuzQSHF%w{6Zl=1kNLXe8fX@kB4jxhPqzCI+J-QS-GNj z=?$nZBIAT*1hV!D7QU^||A8zlj^L@Pl4O^6kOIo|v3#dg97R;&Z}^#a)oeh1^WIM_LI{7R55mYo1rs+Mbf z`IU^j zc=d2GL+pf=L?E6KfB%}twHj`XHYz^~>LY1f2oaM4)jG+KBY7Z`09bdDokr#YQ9oSB zjNs?u1DdCi1T?7mCa}jlX(r*31oQ}=XXf)A1zxidSO(8y6|ciG{EVychz<$#jbFJc zK08wEriQwohh=~EkfyCko8IGvf#nYs!1N2t?(5T zRVbDQ(b8?8j@ErN0i8$;)mgL0=v7?oYj99S(sB`9Ns=EwqJ!38kjO<-M*0UYDKDTU73rOL2R=>`p2Z*L zQZ486uyq=VKA=kFct0L?-yCfCl74HFWh*bsJw%q5*st7A!fTeQD`{tmObW=RNe=*A zwu|O6X`EKxVw34c22sejC^#eqE{Vofi+y3Gk6bld_;0<3Qh$HQL!JstL*C^0Fd9%1 z+6m9%535Xu$%9j&lnrQ8IX;O-;+!zb(2IWi3041d9&$SP#kQI~;(>fkv%uIObiC2& zBzXxv=LrXn)Og0a!W)<`uz{w!ON_jk)bsWf9|TRB+#*7>e@r*IBfQ8|V5IZ7IhhtD zp`6DIB0N!9$tIif9r2%31Wx>9unlC__Q$yap%Wzf{4HFw(Bx=?C4z&(cvDs>MQZGE zfO@ShBxv|ZRe6fv74k~N9Fp%L(DM`8fV$9y48Ma^>XBUIVOm8IMf3aN!e>*A@BJar zATx0hzDH6@Jj-C(gePhUmoJPbx}lBI5Y4#pc84lTvwz1%sn3G_mh-{>$=WjzZecpl+K6^Hsne_$ z<{FIsDd#7>wvB6Z9qMcn65kSpq9zM-Ux%X>^=ce2!g-l-+!2$=Dv+~5=Yx=o^3So@ zoBf5n^@XZyvkC_a3qGPAzs;01iZY;&sX;|+e`BBM`-+7HkR%e1uN46N%!j8tKHm{r zZKdvoyw;RR3-%NU(@0$qKj3>VNnYM+n#(ty%5;hFS%l9b_l+?loo^d22sN9nDUpsh zfw%RAf{r%#c*4cIm){T`O+qw6?KG1*i+ze1*8kM2(BTPz)z<_or37J?WOJVgbHM}y z)@^s`ZLAq>*iW5E868i<^v5)bL)J>1-~Z#@e$GpclW8i&*w`-Moh`E7BxC7WNPhg< zzQc_*BMj?>saTUt+oyto=tew~INNXGUPrFR!&f4YCh`6^lL^Y}h)mrU*kmAu+9x!z zNq2_C)&0Vj(_maB_N%_-;^pNBgf9_3i4(Oqk0rX|@pH6UiNUIUw*;h5X}oN&(T{lf zPIRs_x@B>*g?2+JQVYVOHv~u+^GfSVlGE*L-!bAY0mKPw|0^uIXhfAd$lW;kza|0e zXp<3kO7t=zDFsS7G4`}cvX;Nj7g4s?f{qc zbx0|>vd5z_NgrDxK%5Mzq0&Fram=B2mXM_HsmWTS>u3jZhhPxB`&gxJD;az7uH1ZF z`3V)_BVlbDU9n=m2q#6x3ms0#iIi=HX>C@D4hP!s5=32Ol7|RpGX^P6dwCu&shEz8 z2@LwFP?)h2PwFWoUo~w+v{JM`$c{{wHllqpEn);*xIthV@Z)PX0~<%_p;^Z&lV(9v zKT_Rh2|}?Kf9s`~sh&opnJvI&Y|>FHl4g|osIl0^DlM=tS=9!Wv}p?pw9!eEWSF30 zXR9Qo=qk=b;8nHehe^gtGSOemKsYb$l^Shac+*zBZA1Il$Rf$%3d&TPQ$yY6sf9(q zr*<{l{1+Rg=+}wgaIbyPv5`vpz{LLi&Eo*>-~UK;@8^`MI%8h*4qdU&nD)iAz?)U$ zrjeT;Eu`(agC@F#G88?;Z*F)u11Hp)2=h47*o5js%(5&-%9~kAt|v@2tCb|`5HL;D zpiFI=jpnHQ#LEWK3zQt%V~g z(q9ND`c8e8Ez-wc6`U>^OoZI|oUptnSYH)zTjES=`XvIdkOl-!Lv{!NLSde64rlwv zGKWlALgF|)m78n*{*#m^uXUPEW9FafAMz3q?93n{vuTB6EjU8sjd9;zFbYGkB?JnwjIl^2dbl;W#P3W#i zBh0?KBa7gEYcQf<~>qz#h`aJ~x5ST}Cs~`KARG*AQ;&dR-Y1 zKgt;;>*R|n=YtFNaGx`eGwiH{aObdm%;f4V*D-+i^XZ*zV7wDRG zpX`H-Esb7_(+ZNk!X87 zjnYvxTOn;{)R$FCAV2|XDwJ>ITR^_XPg?2&$a9UW7LI~XCA(|R&~Fj#U<+|*2tu%B zDB_fJnBL^9MRV2IK))>l8KE1>&=|QDkmm&$v@4+iQqh}cM-D>5Ya>BRmiXRJ-`6iJy^n6QxltHgt^@+B&53WJg2pY-*K0 z6c=gLxf^>R($*7Pb4@%Z)lpofWM=U}%!PJS2`H`@i-)*sR`6Q3hVT%-n)$1PiVbjs zJ9-~Naf_j!Vd_ZIibW=yMop8BLU^B3r}x>5Q3wvi;c3w$Mg32T;{Wk08emMvr!(`g zVg)SnNuVK$$6Igej`A+1Xu(`%dY>J51Vm<7rjFz*7tv0dPSfse0jd2ra7V#jq{O&? zpPk8D3b{x?I#=fjND&UYFGe_N!m+Q#=v*z!%D>G6!9^_INO{j!yaO%1rE;u*sWib< z(0j%~584_9s3%b5#mywFcU&+*`P+cI`J&1i>qS5?xU@oNAA=c2R~4l$%o zXYzPk$%W{D@HP1GOjQP{Ak%M%bnFNSbYjFfZj1NK^%@}N^S8smOy%T%b4wa43|P+ zLpP{+zxjm{z&#AN0nffRw!`59A<_-lg&z(0*YPkp$N(YTqtr7PCkG#8M<_VSRu(N{fH!UZFNQ=0N;9LMX%(~~LT8UPNG3qN$#^u{9$ zA{-_}EzQg)=L%0K^lex?9nfoCHJLngQ1WI7>TPx8SqSP1rL@y{ zzy{C0lj*~=8_KR+v|~TpZ5)I)zwN(ieY74EAX$wXo#`V~`{W`A5onhKMiHf|2Hse~ zBugNP%Wj%^m|zqQ0k5FZJ(V1LQXjw{$YlXnKZg!&EugQH|bby|AFY z7`In<_Tx)iawCA0o;c+tHwV`L!upx}3kn+Y zW@sLFCa+|{S?{8C}`rbKEwbp#c zjlbTu)JgL`O<+jzbd2PxC#N!9ZgAk5N&Ze8p}={tg-OGw!8);lHdr1{Hi-KO;w`7y zWEc&6n&|vUIyP(<&2T)|@KjHZpUx|d4gc9n!8_<-k>q;HW1eOCAJO3UmtnTV-4&Y@64hE$J18 zt0Hg2Lq3{%V?#1YRcnF`=^f@l4o%$tQmH9@Zpk)f&L&nPt_rA8O&R$QYD_o5g6m)? zxZG4Im@q;da_CznT=Uo}V25~gEB}R?LPf?^0)_I3a2OahEHhu1hi#G@c(=3RxM7J* zo>15mQIk^;5{-J3a6;1K+qpClS#^M9FGd43>ff39Yx2;0Cuv&XK&ooq=Zm&HzUY4i-yq?jt0;G_A> zyY?teNKAU8JHmR;<6L;BQH2P;Xg3X-g?mx_-Ava#)CPVu+W-g%M11~##_eFN7Y19i)sj{APAg{B{RZ91b=(cSm_eTFhe( z;tQ{lhKf$3;;C(#Zw8XPh@?3w)N{PpQW3Uq-Qp&_d4{ey(tx=_18`Zs-J|du8ZGS8 zF@~89;1!5IL3Iv7#pOqH+mw_W=!)+=qmCRtlv>1(1+boO=h!fFLl6{SL$>&s-9c_! zlFr@x^U4BD7Nc_H6Uz3< zbfn`*jMV{&BvJRp64K>>ScTIxlQk-k!d>cVj7wWb1>5gim~Xb;N8iyZP>g^%3ZMEK zV$-n~Quc-7|I4TU{mot^8QqStVKiv+k(2z$#bwz8$b$QjteKdsT-6g7E(H4$utBOM z+c#Y&mb}%E>>y0&+n7|Z{Rqx+TdtW!>{+WXF7%BOk&Diz-j~TqtWm-=q8U9Bu_81s znkgmMrA@Ki1dH4!ZCsPHsjnK=nqA~kUT1t2L8_Dd3EMvP@*X(`uZT$R7m;&`_X4^s zl}hdz5Ahz7k%=qmaf`EJ>$sDA!O(#O3t*{_KaSaY0mAhC^bc~C{6eRNV5g+cIwNgj zf&tm*Zjj*}^^i?nAlIPEYTQ)wdA@pNoHCJ-zncA6i15Phhy}PY&C{2HM$>BJnbM3Gc5d_%!7LKF3QZ5tsBS!N z_3!0s=`|`-+~bu7x<&F-pRYr740T^XwEESHqwc_$7R9mKvLPeSjNRmwgsP5w%p1E1 zx%9=Q&d!5k0jsPwj=84q+kS9n(mm*QGVJYZ+XZ&cjauoNHu9##xW+E3MaqTl-KFzChp0 zrEML|GH<%8-lxryAZ*ctJ8;VO4R_FJ#UQ+Y@{pY;^r;2y;F2CW$y+J?@Iw^z6{G$n zr*jteDDCiG3`5?F3AO;fxZ^7}*YS!?k;SPxysa~dysZ<+PgT(~<Gh%7@ec(5c3lZ1P>PoWM((Ke zd26!Dzvn~93Aw3oC@+k`?hh$N=X<2=e;myu{}IEu$w+fw<~Q*aq2UgvZ-R4`!GSX+ zEwzjA@=(|Zndl9EdJ$Li_Uyls_N2!p|4??$sDr^j+*o_lecHKe6V*F{R%!aB{W8ws z{iuhVE?eXOK!XW1N?OKhh?e^gdHe{i2&y(2#KcdNf43!G#3tHf`YQ@^mA+_lW#de7V# zGOaY_HzsUmQLW#GNz1PJt^DYj@w2B}4=%`kqTUs>YV+`|RsUXhLmAev`C?5-R8LeN z(Fh(FWcQ}cD70SgeVMc$aM`{ym64C4;RLqV(9Os|D~`;faC9PT>mU5PZRm0No6sFU zSiAMJzW&iCjjwHWSQ3+5aIE|GhN%U|JPw}?+FAN>(bwN_fymX#l?-)SQS&86lGLH0k;gwFj9Wy*5W2 zbeX+KrHcx2Drj@5Pf*Sd<_3H8di8l0ZUcKMvSgz9;n|ly_iU|y>LrzWAGG#^eOP+a zjj>^oYVY&fm>1L^!537s9BbZLDf(_vkNEP0VwGav35NqC>gx`!z5IFqX_X$|)r8MY zdd1~T)hENYEs7Z5@{hw$)H{9}xY^5dq$~UCv*y<4>_g)_TQ-sJ)ad9Zk6La$&&igmaaU>u;=c5`%(L)83}X+wB=@13$KbLNIu9#wh`*)R zV-yXTa-PL0<;v)-r*+axh{BL_t|d9k?Yw-?2KF!AMWxdD#T63~>u@c8U1r&y3x72x zQX>7eb!cUL`c`rdJhf{`;C=RTnCR)*u;zfKh?b`8FY#(_lD;p~NeCv_M%AE;{NIA{ zD0YZFcFulKw-WQj9r2oqUhWgAOhYX$=ximGDu0*~9Mg&yt#M7^q6NdoY>KexflUrh z&FBz9>9aeflV*5Dcn^60hVFCl_9zOp@{Yc04%fnq$G`Hsi*0WLwC6LtiQ^>3cG05b ziUfXt7tH6n=85oIK|ib1{Y%CS49Ls+3-AaN)-k0kaIz{2*x?^i$vk)1Cz-;reJn3aleTSM+Ta?o aXx{Fh`%kLYY!czW3FF=h-xU_U{Qm(sHl%3) From 5b0a799769da9a2ebc662d4aab1f31cf85882c56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=AErvu=20Mihai=20Cristian?= Date: Sat, 10 Jan 2026 00:09:06 +0200 Subject: [PATCH 096/117] fix (#5482) --- src/shell.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shell.html b/src/shell.html index e6c80a39b..e5e26a3b1 100644 --- a/src/shell.html +++ b/src/shell.html @@ -179,7 +179,7 @@ jwE50AGjLCVuS8Yt4H7OgZLKK5EKOsLviEWJSL/+0uMi7gLUSBseYwqEbXvSHCec1CJvZPyHCmYQffaB - + From 8de88c71daa21b943daef1b08b2c39013a868d74 Mon Sep 17 00:00:00 2001 From: lucas150670 Date: Sat, 10 Jan 2026 17:54:54 +0800 Subject: [PATCH 097/117] fix: fall back on dirent filename when all route fails on Android (#5484) --- src/utils.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils.c b/src/utils.c index 82d7d0aa2..28939b4f8 100644 --- a/src/utils.c +++ b/src/utils.c @@ -473,7 +473,9 @@ FILE *android_fopen(const char *fileName, const char *mode) { #undef fopen // Just do a regular open if file is not found in the assets - return fopen(TextFormat("%s/%s", internalDataPath, fileName), mode); + if(fopen(TextFormat("%s/%s", internalDataPath, fileName), mode) == NULL) { + return fopen(fileName, mode); + } #define fopen(name, mode) android_fopen(name, mode) } } From 683330582679397f225212bfcf925684e351954b Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 10 Jan 2026 10:59:22 +0100 Subject: [PATCH 098/117] REVIEWED: `android_fopen()` --- src/utils.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/utils.c b/src/utils.c index 28939b4f8..80c8dec55 100644 --- a/src/utils.c +++ b/src/utils.c @@ -449,6 +449,8 @@ void InitAssetManager(AAssetManager *manager, const char *dataPath) // REF: https://developer.android.com/ndk/reference/group/asset FILE *android_fopen(const char *fileName, const char *mode) { + FILE *file = NULL; + if (mode[0] == 'w') { // NOTE: fopen() is mapped to android_fopen() that only grants read access to @@ -456,7 +458,7 @@ FILE *android_fopen(const char *fileName, const char *mode) // write data when required using the standard stdio FILE access functions // REF: https://stackoverflow.com/questions/11294487/android-writing-saving-files-from-native-code-only #undef fopen - return fopen(TextFormat("%s/%s", internalDataPath, fileName), mode); + file = fopen(TextFormat("%s/%s", internalDataPath, fileName), mode); #define fopen(name, mode) android_fopen(name, mode) } else @@ -467,18 +469,19 @@ FILE *android_fopen(const char *fileName, const char *mode) if (asset != NULL) { // Get pointer to file in the assets - return funopen(asset, android_read, android_write, android_seek, android_close); + file = funopen(asset, android_read, android_write, android_seek, android_close); } else { #undef fopen // Just do a regular open if file is not found in the assets - if(fopen(TextFormat("%s/%s", internalDataPath, fileName), mode) == NULL) { - return fopen(fileName, mode); - } + file = fopen(TextFormat("%s/%s", internalDataPath, fileName), mode); + if (file == NULL) file = fopen(fileName, mode); #define fopen(name, mode) android_fopen(name, mode) } } + + return file; } #endif // PLATFORM_ANDROID From c7de5c9d4b2f0ec0ba049db5ffa4036fa63d5fff Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 10 Jan 2026 11:53:29 +0100 Subject: [PATCH 099/117] Updated examples VCXPROJ UUID, not correctly calculated by REXM --- projects/VS2022/examples/core_keyboard_testbed.vcxproj | 2 +- projects/VS2022/examples/textures_framebuffer_rendering.vcxproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/VS2022/examples/core_keyboard_testbed.vcxproj b/projects/VS2022/examples/core_keyboard_testbed.vcxproj index 3a146b762..2278f4ec5 100644 --- a/projects/VS2022/examples/core_keyboard_testbed.vcxproj +++ b/projects/VS2022/examples/core_keyboard_testbed.vcxproj @@ -51,7 +51,7 @@ - {6B1A933E-71B8-4C1F-9E79-02D98830E671} + {D35D2FDA-B53F-4F70-81CA-24D95812B89C} Win32Proj core_keyboard_testbed 10.0 diff --git a/projects/VS2022/examples/textures_framebuffer_rendering.vcxproj b/projects/VS2022/examples/textures_framebuffer_rendering.vcxproj index 3a7eeeb35..3e845545e 100644 --- a/projects/VS2022/examples/textures_framebuffer_rendering.vcxproj +++ b/projects/VS2022/examples/textures_framebuffer_rendering.vcxproj @@ -51,7 +51,7 @@ - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E} + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C} Win32Proj textures_framebuffer_rendering 10.0 From 2f6feb74d49eab6b3384440c51aff46c40326e44 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 10 Jan 2026 11:53:46 +0100 Subject: [PATCH 100/117] Update raylib.sln --- projects/VS2022/raylib.sln | 106 ++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 54 deletions(-) diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 93c4efaf1..9b088f52d 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -433,9 +433,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_cellular_automata" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_hilbert_curve", "examples\shapes_hilbert_curve.vcxproj", "{DC163251-16C3-4B72-B965-ACDBA0F02BD1}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_keyboard_testbed", "examples\core_keyboard_testbed.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_keyboard_testbed", "examples\core_keyboard_testbed.vcxproj", "{D35D2FDA-B53F-4F70-81CA-24D95812B89C}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_framebuffer_rendering", "examples\textures_framebuffer_rendering.vcxproj", "{2CCCD9E4-9058-4291-BD89-39C979F0CA1E}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_framebuffer_rendering", "examples\textures_framebuffer_rendering.vcxproj", "{F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -5395,54 +5395,54 @@ Global {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x64.Build.0 = Release|x64 {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x86.ActiveCfg = Release|Win32 {DC163251-16C3-4B72-B965-ACDBA0F02BD1}.Release|x86.Build.0 = Release|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.Build.0 = Debug|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.ActiveCfg = Debug|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.Build.0 = Debug|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.ActiveCfg = Debug|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.Build.0 = Debug|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.ActiveCfg = Release|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.Build.0 = Release|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.ActiveCfg = Release|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|ARM64.Build.0 = Debug|ARM64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|x64.ActiveCfg = Debug|x64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|x64.Build.0 = Debug|x64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|x86.ActiveCfg = Debug|Win32 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Debug|x86.Build.0 = Debug|Win32 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|ARM64.ActiveCfg = Release|ARM64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|ARM64.Build.0 = Release|ARM64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|x64.ActiveCfg = Release|x64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|x64.Build.0 = Release|x64 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|x86.ActiveCfg = Release|Win32 - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E}.Release|x86.Build.0 = Release|Win32 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|ARM64.Build.0 = Debug|ARM64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|x64.ActiveCfg = Debug|x64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|x64.Build.0 = Debug|x64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|x86.ActiveCfg = Debug|Win32 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Debug|x86.Build.0 = Debug|Win32 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|ARM64.ActiveCfg = Release|ARM64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|ARM64.Build.0 = Release|ARM64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|x64.ActiveCfg = Release|x64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|x64.Build.0 = Release|x64 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|x86.ActiveCfg = Release|Win32 + {D35D2FDA-B53F-4F70-81CA-24D95812B89C}.Release|x86.Build.0 = Release|Win32 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|ARM64.Build.0 = Debug|ARM64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|x64.ActiveCfg = Debug|x64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|x64.Build.0 = Debug|x64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|x86.ActiveCfg = Debug|Win32 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Debug|x86.Build.0 = Debug|Win32 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|ARM64.ActiveCfg = Release|ARM64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|ARM64.Build.0 = Release|ARM64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|x64.ActiveCfg = Release|x64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|x64.Build.0 = Release|x64 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|x86.ActiveCfg = Release|Win32 + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -5610,7 +5610,7 @@ Global {C54703BF-D68A-480D-BE27-49B62E45D582} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {9CD8BCAD-F212-4BCC-BA98-899743CE3279} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {0981CA28-E4A5-4DF1-987F-A41D09131EFC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {278D8859-20B1-428F-8448-064F46E1F021} + {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {6777EC3C-077C-42FC-B4AD-B799CE55CCE4} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {A61DAD9C-271C-4E95-81AA-DB4CD58564D4} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} @@ -5619,7 +5619,7 @@ Global {3B27F358-2679-4F38-B297-17B536F580BB} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {718FCBD0-591D-448C-B7D5-9F1CA8544E7B} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {19CA0070-B4B2-4394-90B7-D0C259AA35BA} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E} = {278D8859-20B1-428F-8448-064F46E1F021} + {2CCCD9E4-9058-4291-BD89-39C979F0CA1E} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {9DB1F875-6E65-4195-B23F-ED8095C0B99C} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {8E132D5A-2C00-48D0-8747-97E41356F26F} = {278D8859-20B1-428F-8448-064F46E1F021} @@ -5661,8 +5661,6 @@ Global {1F4722E7-F78E-413F-A106-D3490211EA57} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {0A0FC982-6E31-401F-BA77-3C5E8AB02C68} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {DC163251-16C3-4B72-B965-ACDBA0F02BD1} = {278D8859-20B1-428F-8448-064F46E1F021} - {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {2CCCD9E4-9058-4291-BD89-39C979F0CA1E} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} From dd7a1948f1c5b97b6e6e4c80cc0ae17d99ded8c6 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 10 Jan 2026 12:13:07 +0100 Subject: [PATCH 101/117] WARNING: REDESIGN: REMOVED: `utils` module, functionality moved to `rcore` module: logging and file-system #4551 [utils] was created long time ago, when [rcore] contained all the platforms code, the purpose of the file was exposing basic filesystem functionality across modules and also logging mechanism but many things have changed since then and there is no need to keep using this module. - Logging system has been move to [rcore] module and macros are exposed through `config.h` to other modules - File system functionality has also been centralized in [rcore] module that along the years it was already adding more and more file-system functions, now they are all in the same module - Android specific code has been moved to `rcore_android.c`, it had no sense to have specific platform code in `utils`, [rcore] is responsible of all platform code. --- build.zig | 2 +- projects/VS2022/raylib/raylib.vcxproj | 1 - projects/VS2022/raylib/raylib.vcxproj.filters | 3 - src/Makefile | 13 +- src/config.h | 27 +- src/platforms/rcore_android.c | 82 ++- src/platforms/rcore_desktop_glfw.c | 4 - src/platforms/rcore_desktop_rgfw.c | 5 +- src/platforms/rcore_desktop_sdl.c | 3 - src/platforms/rcore_desktop_win32.c | 3 - src/platforms/rcore_drm.c | 3 - src/platforms/rcore_memory.c | 3 - src/platforms/rcore_web.c | 3 - src/platforms/rcore_web_emscripten.c | 3 - src/raudio.c | 2 +- src/raylib.h | 59 +- src/rcore.c | 407 +++++++++++++- src/rmodels.c | 1 - src/rtext.c | 1 - src/rtextures.c | 1 - src/utils.c | 514 ------------------ src/utils.h | 74 --- 22 files changed, 525 insertions(+), 689 deletions(-) delete mode 100644 src/utils.c delete mode 100644 src/utils.h diff --git a/build.zig b/build.zig index 239b10f9e..2e53bfbdc 100644 --- a/build.zig +++ b/build.zig @@ -197,7 +197,7 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. } var c_source_files: std.ArrayList([]const u8) = try .initCapacity(b.allocator, 2); - c_source_files.appendSliceAssumeCapacity(&.{ "src/rcore.c", "src/utils.c" }); + c_source_files.appendSliceAssumeCapacity(&.{ "src/rcore.c" }); if (options.rshapes) { try c_source_files.append(b.allocator, "src/rshapes.c"); diff --git a/projects/VS2022/raylib/raylib.vcxproj b/projects/VS2022/raylib/raylib.vcxproj index 3a7082d77..287410f06 100644 --- a/projects/VS2022/raylib/raylib.vcxproj +++ b/projects/VS2022/raylib/raylib.vcxproj @@ -582,7 +582,6 @@ - diff --git a/projects/VS2022/raylib/raylib.vcxproj.filters b/projects/VS2022/raylib/raylib.vcxproj.filters index 33030fc9c..75cc28a7e 100644 --- a/projects/VS2022/raylib/raylib.vcxproj.filters +++ b/projects/VS2022/raylib/raylib.vcxproj.filters @@ -22,9 +22,6 @@ Source Files - - Source Files - Source Files\Platform Files diff --git a/src/Makefile b/src/Makefile index 89dd759aa..459b79f83 100644 --- a/src/Makefile +++ b/src/Makefile @@ -658,8 +658,7 @@ endif OBJS = rcore.o \ rshapes.o \ rtextures.o \ - rtext.o \ - utils.o + rtext.o ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(USE_EXTERNAL_GLFW),FALSE) @@ -758,7 +757,7 @@ endif rcore.o : platforms/*.c # Compile core module -rcore.o : rcore.c raylib.h rlgl.h utils.h raymath.h rcamera.h rgestures.h +rcore.o : rcore.c raylib.h rlgl.h raymath.h rcamera.h rgestures.h $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) # Compile rglfw module @@ -770,15 +769,11 @@ rshapes.o : rshapes.c raylib.h rlgl.h $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) # Compile textures module -rtextures.o : rtextures.c raylib.h rlgl.h utils.h +rtextures.o : rtextures.c raylib.h rlgl.h $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) # Compile text module -rtext.o : rtext.c raylib.h utils.h - $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) - -# Compile utils module -utils.o : utils.c utils.h +rtext.o : rtext.c raylib.h $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) # Compile models module diff --git a/src/config.h b/src/config.h index 1286e5082..68b42cc0e 100644 --- a/src/config.h +++ b/src/config.h @@ -30,7 +30,7 @@ //------------------------------------------------------------------------------------ // Module selection - Some modules could be avoided -// Mandatory modules: rcore, rlgl, utils +// Mandatory modules: rcore, rlgl //------------------------------------------------------------------------------------ #define SUPPORT_MODULE_RSHAPES 1 #define SUPPORT_MODULE_RTEXTURES 1 @@ -41,6 +41,16 @@ //------------------------------------------------------------------------------------ // Module: rcore - Configuration Flags //------------------------------------------------------------------------------------ +// Standard file io library (stdio.h) included +#define SUPPORT_STANDARD_FILEIO 1 +// Show TRACELOG() output messages +#define SUPPORT_TRACELOG 1 +#if defined(SUPPORT_TRACELOG) + #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) +#else + #define TRACELOG(level, ...) (void)0 +#endif + // Camera module is included (rcamera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital #define SUPPORT_CAMERA_SYSTEM 1 // Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag @@ -72,7 +82,7 @@ // Support for clipboard image loading // NOTE: Only working on SDL3, GLFW (Windows) and RGFW (Windows) -#define SUPPORT_CLIPBOARD_IMAGE 1 +#define SUPPORT_CLIPBOARD_IMAGE 1 // NOTE: Clipboard image loading requires support for some image file formats // TODO: Those defines should probably be removed from here, letting the user manage them @@ -96,6 +106,7 @@ // rcore: Configuration values //------------------------------------------------------------------------------------ +#define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message #define MAX_FILEPATH_CAPACITY 8192 // Maximum file paths capacity #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) @@ -281,16 +292,4 @@ #define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 // Maximum number of audio pool channels -//------------------------------------------------------------------------------------ -// Module: utils - Configuration Flags -//------------------------------------------------------------------------------------ -// Standard file io library (stdio.h) included -#define SUPPORT_STANDARD_FILEIO 1 -// Show TRACELOG() output messages -#define SUPPORT_TRACELOG 1 - -// utils: Configuration values -//------------------------------------------------------------------------------------ -#define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message - #endif // CONFIG_H diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 19b686cef..6122f9a74 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -13,9 +13,6 @@ * - Improvement 01 * - Improvement 02 * -* ADDITIONAL NOTES: -* - TRACELOG() function is located in raylib [utils] module -* * CONFIGURATION: * #define RCORE_PLATFORM_CUSTOM_FLAG * Custom flag for rcore on target platform -not used- @@ -48,7 +45,11 @@ #include // Required for: android_app struct and activity management #include // Required for: AWINDOW_FLAG_FULLSCREEN definition and others +#include // Required for: Android log system: __android_log_vprint() +#include // Required for: AAssetManager //#include // Required for: Android sensors functions (accelerometer, gyroscope, light...) + +#include // Required for: error types #include // Required for: JNIEnv and JavaVM [Used in OpenURL() and GetCurrentMonitor()] #include // Native platform windowing system interface @@ -269,6 +270,17 @@ static GamepadButton AndroidTranslateGamepadButton(int button); static void SetupFramebuffer(int width, int height); // Setup main framebuffer (required by InitPlatform()) +static int android_read(void *cookie, char *buf, int size); +static int android_write(void *cookie, const char *buf, int size); +static fpos_t android_seek(void *cookie, fpos_t offset, int whence); +static int android_close(void *cookie); + +FILE *android_fopen(const char *fileName, const char *mode); // Replacement for fopen() -> Read-only! +FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), int (*writefn)(void *, const char *, int), + fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *)); + +#define fopen(name, mode) android_fopen(name, mode) + //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- @@ -819,8 +831,6 @@ int InitPlatform(void) // Initialize storage system //---------------------------------------------------------------------------- - InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); // Initialize assets manager - CORE.Storage.basePath = platform.app->activity->internalDataPath; // Define base path for storage //---------------------------------------------------------------------------- @@ -1514,4 +1524,66 @@ static void SetupFramebuffer(int width, int height) } } +// Replacement for fopen() +// REF: https://developer.android.com/ndk/reference/group/asset +FILE *android_fopen(const char *fileName, const char *mode) +{ + FILE *file = NULL; + + if (mode[0] == 'w') + { + // NOTE: fopen() is mapped to android_fopen() that only grants read access to + // assets directory through AAssetManager but we want to also be able to + // write data when required using the standard stdio FILE access functions + // REF: https://stackoverflow.com/questions/11294487/android-writing-saving-files-from-native-code-only + #undef fopen + file = fopen(TextFormat("%s/%s", platform.app->activity->internalDataPath, fileName), mode); + #define fopen(name, mode) android_fopen(name, mode) + } + else + { + // NOTE: AAsset provides access to read-only asset + AAsset *asset = AAssetManager_open(platform.app->activity->assetManager, fileName, AASSET_MODE_UNKNOWN); + + if (asset != NULL) + { + // Get pointer to file in the assets + file = funopen(asset, android_read, android_write, android_seek, android_close); + } + else + { + #undef fopen + // Just do a regular open if file is not found in the assets + file = fopen(TextFormat("%s/%s", platform.app->activity->internalDataPath, fileName), mode); + if (file == NULL) file = fopen(fileName, mode); + #define fopen(name, mode) android_fopen(name, mode) + } + } + + return file; +} + +static int android_read(void *cookie, char *data, int dataSize) +{ + return AAsset_read((AAsset *)cookie, data, dataSize); +} + +static int android_write(void *cookie, const char *data, int dataSize) +{ + TRACELOG(LOG_WARNING, "ANDROID: Failed to provide write access to APK"); + + return EACCES; +} + +static fpos_t android_seek(void *cookie, fpos_t offset, int whence) +{ + return AAsset_seek((AAsset *)cookie, offset, whence); +} + +static int android_close(void *cookie) +{ + AAsset_close((AAsset *)cookie); + return 0; +} + // EOF diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 84b021573..f39e256aa 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -16,9 +16,6 @@ * - Improvement 01 * - Improvement 02 * -* ADDITIONAL NOTES: -* - TRACELOG() function is located in raylib [utils] module -* * CONFIGURATION: * #define RCORE_PLATFORM_CUSTOM_FLAG * Custom flag for rcore on target platform -not used- @@ -1364,7 +1361,6 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Function wrappers around RL_*alloc macros, used by glfwInitAllocator() inside of InitPlatform() // We need to provide these because GLFWallocator expects function pointers with specific signatures -// Similar wrappers exist in utils.c but we cannot reuse them here due to declaration mismatch // REF: https://www.glfw.org/docs/latest/intro_guide.html#init_allocator static void *AllocateWrapper(size_t size, void *user) { diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 05960c14b..d1518b909 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -13,10 +13,7 @@ * - TODO * * POSSIBLE IMPROVEMENTS: -* - TODO -* -* ADDITIONAL NOTES: -* - TRACELOG() function is located in raylib [utils] module +* - TBD * * CONFIGURATION: * #define RCORE_PLATFORM_RGFW diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 0279c0c28..eabea6bfe 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -15,9 +15,6 @@ * - Improvement 01 * - Improvement 02 * -* ADDITIONAL NOTES: -* - TRACELOG() function is located in raylib [utils] module -* * CONFIGURATION: * #define RCORE_PLATFORM_CUSTOM_FLAG * Custom flag for rcore on target platform -not used- diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 7ef01a1a0..9f33dce1b 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -13,9 +13,6 @@ * - Improvement 01 * - Improvement 02 * -* ADDITIONAL NOTES: -* - TRACELOG() function is located in raylib [utils] module -* * CONFIGURATION: * #define RCORE_PLATFORM_CUSTOM_FLAG * Custom flag for rcore on target platform -not used- diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index eedb915e2..2e224ad92 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -13,9 +13,6 @@ * - Improvement 01 * - Improvement 02 * -* ADDITIONAL NOTES: -* - TRACELOG() function is located in raylib [utils] module -* * CONFIGURATION: * #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only) * Reconfigure standard input to receive key inputs, works with SSH connection diff --git a/src/platforms/rcore_memory.c b/src/platforms/rcore_memory.c index 04d343164..c9409a750 100644 --- a/src/platforms/rcore_memory.c +++ b/src/platforms/rcore_memory.c @@ -13,9 +13,6 @@ * - Improvement 01 * - Improvement 02 * -* ADDITIONAL NOTES: -* - TRACELOG() function is located in raylib [utils] module -* * CONFIGURATION: * #define RCORE_PLATFORM_CUSTOM_FLAG * Custom flag for rcore on target platform -not used- diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 2b51804ef..f1922600f 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -12,9 +12,6 @@ * POSSIBLE IMPROVEMENTS: * - Replace glfw3 dependency by direct browser API calls (same as library_glfw3.js) * -* ADDITIONAL NOTES: -* - TRACELOG() function is located in raylib [utils] module -* * CONFIGURATION: * #define RCORE_PLATFORM_CUSTOM_FLAG * Custom flag for rcore on target platform -not used- diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c index 5fdcdbefc..36b8e964a 100644 --- a/src/platforms/rcore_web_emscripten.c +++ b/src/platforms/rcore_web_emscripten.c @@ -11,9 +11,6 @@ * POSSIBLE IMPROVEMENTS: * - TBD * -* ADDITIONAL NOTES: -* - TRACELOG() function is located in raylib [utils] module -* * CONFIGURATION: * #define RCORE_PLATFORM_CUSTOM_FLAG * Custom flag for rcore on target platform -not used- diff --git a/src/raudio.c b/src/raudio.c index cfb86cdbd..c25ad1f02 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -78,7 +78,7 @@ #if !defined(EXTERNAL_CONFIG_FLAGS) #include "config.h" // Defines module configuration flags #endif - #include "utils.h" // Required for: fopen() Android mapping + //#include "utils.h" // Required for: fopen() Android mapping #endif #if defined(SUPPORT_MODULE_RAUDIO) || defined(RAUDIO_STANDALONE) diff --git a/src/raylib.h b/src/raylib.h index 6eae8411e..177138dc9 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1074,47 +1074,41 @@ RLAPI Matrix GetCameraMatrix(Camera camera); // Get c RLAPI Matrix GetCameraMatrix2D(Camera2D camera); // Get camera 2d transform matrix // Timing-related functions -RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) -RLAPI float GetFrameTime(void); // Get time in seconds for last frame drawn (delta time) -RLAPI double GetTime(void); // Get elapsed time in seconds since InitWindow() -RLAPI int GetFPS(void); // Get current FPS +RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) +RLAPI float GetFrameTime(void); // Get time in seconds for last frame drawn (delta time) +RLAPI double GetTime(void); // Get elapsed time in seconds since InitWindow() +RLAPI int GetFPS(void); // Get current FPS // Custom frame control functions // NOTE: Those functions are intended for advanced users that want full control over the frame processing // By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents() // To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL -RLAPI void SwapScreenBuffer(void); // Swap back buffer with front buffer (screen drawing) -RLAPI void PollInputEvents(void); // Register all input events -RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) +RLAPI void SwapScreenBuffer(void); // Swap back buffer with front buffer (screen drawing) +RLAPI void PollInputEvents(void); // Register all input events +RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) // Random values generation functions -RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator -RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included) +RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator +RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included) RLAPI int *LoadRandomSequence(unsigned int count, int min, int max); // Load random values sequence, no values repeated -RLAPI void UnloadRandomSequence(int *sequence); // Unload random values sequence +RLAPI void UnloadRandomSequence(int *sequence); // Unload random values sequence // Misc. functions -RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) -RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) -RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) +RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) +RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) +RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) -// NOTE: Following functions implemented in module [utils] -//------------------------------------------------------------------ -RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) -RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level -RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator -RLAPI void *MemRealloc(void *ptr, unsigned int size); // Internal memory reallocator -RLAPI void MemFree(void *ptr); // Internal memory free +// Logging system +RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level +RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) +RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set custom trace log -// Set custom callbacks -// WARNING: Callbacks setup is intended for advanced users -RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set custom trace log -RLAPI void SetLoadFileDataCallback(LoadFileDataCallback callback); // Set custom file binary data loader -RLAPI void SetSaveFileDataCallback(SaveFileDataCallback callback); // Set custom file binary data saver -RLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback); // Set custom file text data loader -RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver +// Memory management, using internal allocators +RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator +RLAPI void *MemRealloc(void *ptr, unsigned int size); // Internal memory reallocator +RLAPI void MemFree(void *ptr); // Internal memory free -// Files management functions +// File system management functions RLAPI unsigned char *LoadFileData(const char *fileName, int *dataSize); // Load file data as byte array (read) RLAPI void UnloadFileData(unsigned char *data); // Unload file data allocated by LoadFileData() RLAPI bool SaveFileData(const char *fileName, void *data, int dataSize); // Save data to file from byte array (write), returns true on success @@ -1122,9 +1116,14 @@ RLAPI bool ExportDataAsCode(const unsigned char *data, int dataSize, const char RLAPI char *LoadFileText(const char *fileName); // Load text data from file (read), returns a '\0' terminated string RLAPI void UnloadFileText(char *text); // Unload file text data allocated by LoadFileText() RLAPI bool SaveFileText(const char *fileName, const char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success -//------------------------------------------------------------------ -// File system functions +// File access custom callbacks +// WARNING: Callbacks setup is intended for advanced users +RLAPI void SetLoadFileDataCallback(LoadFileDataCallback callback); // Set custom file binary data loader +RLAPI void SetSaveFileDataCallback(SaveFileDataCallback callback); // Set custom file binary data saver +RLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback); // Set custom file text data loader +RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver + RLAPI int FileRename(const char *fileName, const char *fileRename); // Rename file (if exists) RLAPI int FileRemove(const char *fileName); // Remove file (if exists) RLAPI int FileCopy(const char *srcPath, const char *dstPath); // Copy file from one path to another, dstPath created if it doesn't exist diff --git a/src/rcore.c b/src/rcore.c index b7fcbf4a0..7837f8c4e 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -109,11 +109,10 @@ #include "config.h" // Defines module configuration flags #endif -#include "utils.h" // Required for: TRACELOG() macros - -#include // Required for: srand(), rand(), atexit() -#include // Required for: sprintf() [Used in OpenURL()] -#include // Required for: strlen(), strncpy(), strcmp(), strrchr(), memset() +#include // Required for: srand(), rand(), atexit(), exit() +#include // Required for: FILE, fopen(), fseek(), ftell(), fread(), fwrite(), fprintf(), vprintf(), fclose(), sprintf() [Used in OpenURL()] +#include // Required for: strlen(), strncpy(), strcmp(), strrchr(), memset(), strcat() +#include // Required for: va_list, va_start(), va_end() [Used in TraceLog()] #include // Required for: time() [Used in InitTimer()] #include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] @@ -217,6 +216,10 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- +#ifndef MAX_TRACELOG_MSG_LENGTH + #define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message +#endif + #ifndef MAX_FILEPATH_CAPACITY #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath #endif @@ -387,10 +390,18 @@ typedef struct CoreData { //---------------------------------------------------------------------------------- RLAPI const char *raylib_version = RAYLIB_VERSION; // raylib version exported symbol, required for some bindings -CoreData CORE = { 0 }; // Global CORE state context +CoreData CORE = { 0 }; // Global CORE state context + +static int logTypeLevel = LOG_INFO; // Minimum log type level + +static TraceLogCallback traceLog = NULL; // TraceLog callback function pointer +static LoadFileDataCallback loadFileData = NULL; // LoadFileData callback function pointer +static SaveFileDataCallback saveFileData = NULL; // SaveFileText callback function pointer +static LoadFileTextCallback loadFileText = NULL; // LoadFileText callback function pointer +static SaveFileTextCallback saveFileText = NULL; // SaveFileText callback function pointer #if defined(SUPPORT_SCREEN_CAPTURE) -static int screenshotCounter = 0; // Screenshots counter +static int screenshotCounter = 0; // Screenshots counter #endif #if defined(SUPPORT_AUTOMATION_EVENTS) @@ -1857,9 +1868,389 @@ void SetConfigFlags(unsigned int flags) FLAG_SET(CORE.Window.flags, flags); } +// void OpenURL(const char *url); // Defined per platform + //---------------------------------------------------------------------------------- -// Module Functions Definition: File system +// Module Functions Definition: Logging system //---------------------------------------------------------------------------------- +// Set the current threshold (minimum) log level +void SetTraceLogLevel(int logType) { logTypeLevel = logType; } + +// Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) +void TraceLog(int logType, const char *text, ...) +{ +#if defined(SUPPORT_TRACELOG) + // Message has level below current threshold, don't emit + if ((logType < logTypeLevel) || (text == NULL)) return; + + va_list args; + va_start(args, text); + + if (traceLog) + { + traceLog(logType, text, args); + va_end(args); + return; + } + +#if defined(PLATFORM_ANDROID) + switch (logType) + { + case LOG_TRACE: __android_log_vprint(ANDROID_LOG_VERBOSE, "raylib", text, args); break; + case LOG_DEBUG: __android_log_vprint(ANDROID_LOG_DEBUG, "raylib", text, args); break; + case LOG_INFO: __android_log_vprint(ANDROID_LOG_INFO, "raylib", text, args); break; + case LOG_WARNING: __android_log_vprint(ANDROID_LOG_WARN, "raylib", text, args); break; + case LOG_ERROR: __android_log_vprint(ANDROID_LOG_ERROR, "raylib", text, args); break; + case LOG_FATAL: __android_log_vprint(ANDROID_LOG_FATAL, "raylib", text, args); break; + default: break; + } +#else + char buffer[MAX_TRACELOG_MSG_LENGTH] = { 0 }; + + switch (logType) + { + case LOG_TRACE: strncpy(buffer, "TRACE: ", 8); break; + case LOG_DEBUG: strncpy(buffer, "DEBUG: ", 8); break; + case LOG_INFO: strncpy(buffer, "INFO: ", 7); break; + case LOG_WARNING: strncpy(buffer, "WARNING: ", 10); break; + case LOG_ERROR: strncpy(buffer, "ERROR: ", 8); break; + case LOG_FATAL: strncpy(buffer, "FATAL: ", 8); break; + default: break; + } + + unsigned int textLength = (unsigned int)strlen(text); + memcpy(buffer + strlen(buffer), text, (textLength < (MAX_TRACELOG_MSG_LENGTH - 12))? textLength : (MAX_TRACELOG_MSG_LENGTH - 12)); + strcat(buffer, "\n"); + vprintf(buffer, args); + fflush(stdout); +#endif + + va_end(args); + + if (logType == LOG_FATAL) exit(EXIT_FAILURE); // If fatal logging, exit program + +#endif // SUPPORT_TRACELOG +} + +// Set custom trace log +void SetTraceLogCallback(TraceLogCallback callback) +{ + traceLog = callback; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Memory management +//---------------------------------------------------------------------------------- +// Internal memory allocator +// NOTE: Initializes to zero by default +void *MemAlloc(unsigned int size) +{ + void *ptr = RL_CALLOC(size, 1); + return ptr; +} + +// Internal memory reallocator +void *MemRealloc(void *ptr, unsigned int size) +{ + void *ret = RL_REALLOC(ptr, size); + return ret; +} + +// Internal memory free +void MemFree(void *ptr) +{ + RL_FREE(ptr); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: File System management +//---------------------------------------------------------------------------------- +// Load data from file into a buffer +unsigned char *LoadFileData(const char *fileName, int *dataSize) +{ + unsigned char *data = NULL; + *dataSize = 0; + + if (fileName != NULL) + { + if (loadFileData) + { + data = loadFileData(fileName, dataSize); + return data; + } +#if defined(SUPPORT_STANDARD_FILEIO) + FILE *file = fopen(fileName, "rb"); + + if (file != NULL) + { + // WARNING: On binary streams SEEK_END could not be found, + // using fseek() and ftell() could not work in some (rare) cases + fseek(file, 0, SEEK_END); + int size = ftell(file); // WARNING: ftell() returns 'long int', maximum size returned is INT_MAX (2147483647 bytes) + fseek(file, 0, SEEK_SET); + + if (size > 0) + { + data = (unsigned char *)RL_CALLOC(size, sizeof(unsigned char)); + + if (data != NULL) + { + // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] + size_t count = fread(data, sizeof(unsigned char), size, file); + + // WARNING: fread() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation) + // dataSize is unified along raylib as a 'int' type, so, for file-sizes > INT_MAX (2147483647 bytes) we have a limitation + if (count > 2147483647) + { + TRACELOG(LOG_WARNING, "FILEIO: [%s] File is bigger than 2147483647 bytes, avoid using LoadFileData()", fileName); + + RL_FREE(data); + data = NULL; + } + else + { + *dataSize = (int)count; + + if ((*dataSize) != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded (%i bytes out of %i)", fileName, dataSize, count); + else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); + } + } + else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName); + } + else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read file", fileName); + + fclose(file); + } + else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName); +#else + TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback"); +#endif + } + else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); + + return data; +} + +// Unload file data allocated by LoadFileData() +void UnloadFileData(unsigned char *data) +{ + RL_FREE(data); +} + +// Save data to file from buffer +bool SaveFileData(const char *fileName, void *data, int dataSize) +{ + bool success = false; + + if (fileName != NULL) + { + if (saveFileData) + { + return saveFileData(fileName, data, dataSize); + } +#if defined(SUPPORT_STANDARD_FILEIO) + FILE *file = fopen(fileName, "wb"); + + if (file != NULL) + { + // WARNING: fwrite() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation) + // and expects a size_t input value but as dataSize is limited to INT_MAX (2147483647 bytes), there shouldn't be a problem + int count = (int)fwrite(data, sizeof(unsigned char), dataSize, file); + + if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName); + else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName); + else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName); + + int result = fclose(file); + if (result == 0) success = true; + } + else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName); +#else + TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback"); +#endif + } + else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); + + return success; +} + +// Export data to code (.h), returns true on success +bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName) +{ + bool success = false; + +#ifndef TEXT_BYTES_PER_LINE + #define TEXT_BYTES_PER_LINE 20 +#endif + + // NOTE: Text data buffer size is estimated considering raw data size in bytes + // and requiring 6 char bytes for every byte: "0x00, " + char *txtData = (char *)RL_CALLOC(dataSize*6 + 2000, sizeof(char)); + + int byteCount = 0; + byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n"); + byteCount += sprintf(txtData + byteCount, "// //\n"); + byteCount += sprintf(txtData + byteCount, "// DataAsCode exporter v1.0 - Raw data exported as an array of bytes //\n"); + byteCount += sprintf(txtData + byteCount, "// //\n"); + byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n"); + byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n"); + byteCount += sprintf(txtData + byteCount, "// //\n"); + byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2022-2026 Ramon Santamaria (@raysan5) //\n"); + byteCount += sprintf(txtData + byteCount, "// //\n"); + byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n"); + + // Get file name from path + char varFileName[256] = { 0 }; + strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1); + for (int i = 0; varFileName[i] != '\0'; i++) + { + // Convert variable name to uppercase + if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; } + // Replace non valid character for C identifier with '_' + else if (varFileName[i] == '.' || varFileName[i] == '-' || varFileName[i] == '?' || varFileName[i] == '!' || varFileName[i] == '+') { varFileName[i] = '_'; } + } + + byteCount += sprintf(txtData + byteCount, "#define %s_DATA_SIZE %i\n\n", varFileName, dataSize); + + byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%s_DATA_SIZE] = { ", varFileName, varFileName); + for (int i = 0; i < (dataSize - 1); i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), data[i]); + byteCount += sprintf(txtData + byteCount, "0x%x };\n", data[dataSize - 1]); + + // NOTE: Text data size exported is determined by '\0' (NULL) character + success = SaveFileText(fileName, txtData); + + RL_FREE(txtData); + + if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Data as code exported successfully", fileName); + else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export data as code", fileName); + + return success; +} + +// Load text data from file, returns a '\0' terminated string +// NOTE: text chars array should be freed manually +char *LoadFileText(const char *fileName) +{ + char *text = NULL; + + if (fileName != NULL) + { + if (loadFileText) + { + text = loadFileText(fileName); + return text; + } +#if defined(SUPPORT_STANDARD_FILEIO) + FILE *file = fopen(fileName, "rt"); + + if (file != NULL) + { + // WARNING: When reading a file as 'text' file, + // text mode causes carriage return-linefeed translation... + // ...but using fseek() should return correct byte-offset + fseek(file, 0, SEEK_END); + unsigned int size = (unsigned int)ftell(file); + fseek(file, 0, SEEK_SET); + + if (size > 0) + { + text = (char *)RL_CALLOC(size + 1, sizeof(char)); + + if (text != NULL) + { + unsigned int count = (unsigned int)fread(text, sizeof(char), size, file); + + // WARNING: \r\n is converted to \n on reading, so, + // read bytes count gets reduced by the number of lines + if (count < size) text = (char *)RL_REALLOC(text, count + 1); + + // Zero-terminate the string + text[count] = '\0'; + + TRACELOG(LOG_INFO, "FILEIO: [%s] Text file loaded successfully", fileName); + } + else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName); + } + else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read text file", fileName); + + fclose(file); + } + else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName); +#else + TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback"); +#endif + } + else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); + + return text; +} + +// Unload file text data allocated by LoadFileText() +void UnloadFileText(char *text) +{ + RL_FREE(text); +} + +// Save text data to file (write), string must be '\0' terminated +bool SaveFileText(const char *fileName, const char *text) +{ + bool success = false; + + if (fileName != NULL) + { + if (saveFileText) + { + return saveFileText(fileName, text); + } +#if defined(SUPPORT_STANDARD_FILEIO) + FILE *file = fopen(fileName, "wt"); + + if (file != NULL) + { + int count = fprintf(file, "%s", text); + + if (count < 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName); + else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName); + + int result = fclose(file); + if (result == 0) success = true; + } + else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName); +#else + TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback"); +#endif + } + else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); + + return success; +} + +// File access custom callbacks +// WARNING: Callbacks setup is intended for advanced users + +// Set custom file binary data loader +void SetLoadFileDataCallback(LoadFileDataCallback callback) +{ + loadFileData = callback; +} + +// Set custom file binary data saver +void SetSaveFileDataCallback(SaveFileDataCallback callback) +{ + saveFileData = callback; +} + +// Set custom file text data loader +void SetLoadFileTextCallback(LoadFileTextCallback callback) +{ + loadFileText = callback; +} + +// Set custom file text data saver +void SetSaveFileTextCallback(SaveFileTextCallback callback) +{ + saveFileText = callback; +} // Rename file (if exists) // NOTE: Only rename file name required, not full path diff --git a/src/rmodels.c b/src/rmodels.c index 20287a935..76efa3e4a 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -49,7 +49,6 @@ #if defined(SUPPORT_MODULE_RMODELS) -#include "utils.h" // Required for: TRACELOG(), LoadFileData(), LoadFileText(), SaveFileText() #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 #include "raymath.h" // Required for: Vector3, Quaternion and Matrix functionality diff --git a/src/rtext.c b/src/rtext.c index 8413d62bf..7a1b3c027 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -62,7 +62,6 @@ #if defined(SUPPORT_MODULE_RTEXT) -#include "utils.h" // Required for: LoadFile*() #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 -> Only DrawTextPro() #include // Required for: malloc(), free() diff --git a/src/rtextures.c b/src/rtextures.c index 8d4128d4e..fa03d193b 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -70,7 +70,6 @@ #if defined(SUPPORT_MODULE_RTEXTURES) -#include "utils.h" // Required for: TRACELOG() #include "rlgl.h" // OpenGL abstraction layer to multiple versions #include // Required for: malloc(), calloc(), free() diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index 80c8dec55..000000000 --- a/src/utils.c +++ /dev/null @@ -1,514 +0,0 @@ -/********************************************************************************************** -* -* raylib.utils - Some common utility functions -* -* CONFIGURATION: -* #define SUPPORT_TRACELOG -* Show TraceLog() output messages -* NOTE: By default LOG_DEBUG traces not shown -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2014-2026 Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -#include "raylib.h" // WARNING: Required for: LogType enum - -// Check if config flags have been externally provided on compilation line -#if !defined(EXTERNAL_CONFIG_FLAGS) - #include "config.h" // Defines module configuration flags -#endif - -#include "utils.h" - -#if defined(PLATFORM_ANDROID) - #include // Required for: Android error types - #include // Required for: Android log system: __android_log_vprint() - #include // Required for: Android assets manager: AAsset, AAssetManager_open()... -#endif - -#include // Required for: exit() -#include // Required for: FILE, fopen(), fseek(), ftell(), fread(), fwrite(), fprintf(), vprintf(), fclose() -#include // Required for: va_list, va_start(), va_end() -#include // Required for: strcpy(), strcat() - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#ifndef MAX_TRACELOG_MSG_LENGTH - #define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message -#endif - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -static int logTypeLevel = LOG_INFO; // Minimum log type level - -static TraceLogCallback traceLog = NULL; // TraceLog callback function pointer -static LoadFileDataCallback loadFileData = NULL; // LoadFileData callback function pointer -static SaveFileDataCallback saveFileData = NULL; // SaveFileText callback function pointer -static LoadFileTextCallback loadFileText = NULL; // LoadFileText callback function pointer -static SaveFileTextCallback saveFileText = NULL; // SaveFileText callback function pointer - -//---------------------------------------------------------------------------------- -// Functions to set internal callbacks -//---------------------------------------------------------------------------------- -void SetTraceLogCallback(TraceLogCallback callback) { traceLog = callback; } // Set custom trace log -void SetLoadFileDataCallback(LoadFileDataCallback callback) { loadFileData = callback; } // Set custom file data loader -void SetSaveFileDataCallback(SaveFileDataCallback callback) { saveFileData = callback; } // Set custom file data saver -void SetLoadFileTextCallback(LoadFileTextCallback callback) { loadFileText = callback; } // Set custom file text loader -void SetSaveFileTextCallback(SaveFileTextCallback callback) { saveFileText = callback; } // Set custom file text saver - -#if defined(PLATFORM_ANDROID) -static AAssetManager *assetManager = NULL; // Android assets manager pointer -static const char *internalDataPath = NULL; // Android internal data path -#endif - -//---------------------------------------------------------------------------------- -// Module Internal Functions Declaration -//---------------------------------------------------------------------------------- -#if defined(PLATFORM_ANDROID) -FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), int (*writefn)(void *, const char *, int), - fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *)); - -static int android_read(void *cookie, char *buf, int size); -static int android_write(void *cookie, const char *buf, int size); -static fpos_t android_seek(void *cookie, fpos_t offset, int whence); -static int android_close(void *cookie); -#endif - -//---------------------------------------------------------------------------------- -// Module Functions Definition -//---------------------------------------------------------------------------------- -// Set the current threshold (minimum) log level -void SetTraceLogLevel(int logType) { logTypeLevel = logType; } - -// Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) -void TraceLog(int logType, const char *text, ...) -{ -#if defined(SUPPORT_TRACELOG) - // Message has level below current threshold, don't emit - if ((logType < logTypeLevel) || (text == NULL)) return; - - va_list args; - va_start(args, text); - - if (traceLog) - { - traceLog(logType, text, args); - va_end(args); - return; - } - -#if defined(PLATFORM_ANDROID) - switch (logType) - { - case LOG_TRACE: __android_log_vprint(ANDROID_LOG_VERBOSE, "raylib", text, args); break; - case LOG_DEBUG: __android_log_vprint(ANDROID_LOG_DEBUG, "raylib", text, args); break; - case LOG_INFO: __android_log_vprint(ANDROID_LOG_INFO, "raylib", text, args); break; - case LOG_WARNING: __android_log_vprint(ANDROID_LOG_WARN, "raylib", text, args); break; - case LOG_ERROR: __android_log_vprint(ANDROID_LOG_ERROR, "raylib", text, args); break; - case LOG_FATAL: __android_log_vprint(ANDROID_LOG_FATAL, "raylib", text, args); break; - default: break; - } -#else - char buffer[MAX_TRACELOG_MSG_LENGTH] = { 0 }; - - switch (logType) - { - case LOG_TRACE: strcpy(buffer, "TRACE: "); break; - case LOG_DEBUG: strcpy(buffer, "DEBUG: "); break; - case LOG_INFO: strcpy(buffer, "INFO: "); break; - case LOG_WARNING: strcpy(buffer, "WARNING: "); break; - case LOG_ERROR: strcpy(buffer, "ERROR: "); break; - case LOG_FATAL: strcpy(buffer, "FATAL: "); break; - default: break; - } - - unsigned int textLength = (unsigned int)strlen(text); - memcpy(buffer + strlen(buffer), text, (textLength < (MAX_TRACELOG_MSG_LENGTH - 12))? textLength : (MAX_TRACELOG_MSG_LENGTH - 12)); - strcat(buffer, "\n"); - vprintf(buffer, args); - fflush(stdout); -#endif - - va_end(args); - - if (logType == LOG_FATAL) exit(EXIT_FAILURE); // If fatal logging, exit program - -#endif // SUPPORT_TRACELOG -} - -// Internal memory allocator -// NOTE: Initializes to zero by default -void *MemAlloc(unsigned int size) -{ - void *ptr = RL_CALLOC(size, 1); - return ptr; -} - -// Internal memory reallocator -void *MemRealloc(void *ptr, unsigned int size) -{ - void *ret = RL_REALLOC(ptr, size); - return ret; -} - -// Internal memory free -void MemFree(void *ptr) -{ - RL_FREE(ptr); -} - -// Load data from file into a buffer -unsigned char *LoadFileData(const char *fileName, int *dataSize) -{ - unsigned char *data = NULL; - *dataSize = 0; - - if (fileName != NULL) - { - if (loadFileData) - { - data = loadFileData(fileName, dataSize); - return data; - } -#if defined(SUPPORT_STANDARD_FILEIO) - FILE *file = fopen(fileName, "rb"); - - if (file != NULL) - { - // WARNING: On binary streams SEEK_END could not be found, - // using fseek() and ftell() could not work in some (rare) cases - fseek(file, 0, SEEK_END); - int size = ftell(file); // WARNING: ftell() returns 'long int', maximum size returned is INT_MAX (2147483647 bytes) - fseek(file, 0, SEEK_SET); - - if (size > 0) - { - data = (unsigned char *)RL_CALLOC(size, sizeof(unsigned char)); - - if (data != NULL) - { - // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] - size_t count = fread(data, sizeof(unsigned char), size, file); - - // WARNING: fread() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation) - // dataSize is unified along raylib as a 'int' type, so, for file-sizes > INT_MAX (2147483647 bytes) we have a limitation - if (count > 2147483647) - { - TRACELOG(LOG_WARNING, "FILEIO: [%s] File is bigger than 2147483647 bytes, avoid using LoadFileData()", fileName); - - RL_FREE(data); - data = NULL; - } - else - { - *dataSize = (int)count; - - if ((*dataSize) != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded (%i bytes out of %i)", fileName, dataSize, count); - else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); - } - } - else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName); - } - else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read file", fileName); - - fclose(file); - } - else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName); -#else - TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback"); -#endif - } - else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); - - return data; -} - -// Unload file data allocated by LoadFileData() -void UnloadFileData(unsigned char *data) -{ - RL_FREE(data); -} - -// Save data to file from buffer -bool SaveFileData(const char *fileName, void *data, int dataSize) -{ - bool success = false; - - if (fileName != NULL) - { - if (saveFileData) - { - return saveFileData(fileName, data, dataSize); - } -#if defined(SUPPORT_STANDARD_FILEIO) - FILE *file = fopen(fileName, "wb"); - - if (file != NULL) - { - // WARNING: fwrite() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation) - // and expects a size_t input value but as dataSize is limited to INT_MAX (2147483647 bytes), there shouldn't be a problem - int count = (int)fwrite(data, sizeof(unsigned char), dataSize, file); - - if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName); - else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName); - else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName); - - int result = fclose(file); - if (result == 0) success = true; - } - else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName); -#else - TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback"); -#endif - } - else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); - - return success; -} - -// Export data to code (.h), returns true on success -bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName) -{ - bool success = false; - -#ifndef TEXT_BYTES_PER_LINE - #define TEXT_BYTES_PER_LINE 20 -#endif - - // NOTE: Text data buffer size is estimated considering raw data size in bytes - // and requiring 6 char bytes for every byte: "0x00, " - char *txtData = (char *)RL_CALLOC(dataSize*6 + 2000, sizeof(char)); - - int byteCount = 0; - byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n"); - byteCount += sprintf(txtData + byteCount, "// //\n"); - byteCount += sprintf(txtData + byteCount, "// DataAsCode exporter v1.0 - Raw data exported as an array of bytes //\n"); - byteCount += sprintf(txtData + byteCount, "// //\n"); - byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n"); - byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n"); - byteCount += sprintf(txtData + byteCount, "// //\n"); - byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2022-2026 Ramon Santamaria (@raysan5) //\n"); - byteCount += sprintf(txtData + byteCount, "// //\n"); - byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n"); - - // Get file name from path - char varFileName[256] = { 0 }; - strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1); - for (int i = 0; varFileName[i] != '\0'; i++) - { - // Convert variable name to uppercase - if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; } - // Replace non valid character for C identifier with '_' - else if (varFileName[i] == '.' || varFileName[i] == '-' || varFileName[i] == '?' || varFileName[i] == '!' || varFileName[i] == '+') { varFileName[i] = '_'; } - } - - byteCount += sprintf(txtData + byteCount, "#define %s_DATA_SIZE %i\n\n", varFileName, dataSize); - - byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%s_DATA_SIZE] = { ", varFileName, varFileName); - for (int i = 0; i < (dataSize - 1); i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), data[i]); - byteCount += sprintf(txtData + byteCount, "0x%x };\n", data[dataSize - 1]); - - // NOTE: Text data size exported is determined by '\0' (NULL) character - success = SaveFileText(fileName, txtData); - - RL_FREE(txtData); - - if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Data as code exported successfully", fileName); - else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export data as code", fileName); - - return success; -} - -// Load text data from file, returns a '\0' terminated string -// NOTE: text chars array should be freed manually -char *LoadFileText(const char *fileName) -{ - char *text = NULL; - - if (fileName != NULL) - { - if (loadFileText) - { - text = loadFileText(fileName); - return text; - } -#if defined(SUPPORT_STANDARD_FILEIO) - FILE *file = fopen(fileName, "rt"); - - if (file != NULL) - { - // WARNING: When reading a file as 'text' file, - // text mode causes carriage return-linefeed translation... - // ...but using fseek() should return correct byte-offset - fseek(file, 0, SEEK_END); - unsigned int size = (unsigned int)ftell(file); - fseek(file, 0, SEEK_SET); - - if (size > 0) - { - text = (char *)RL_CALLOC(size + 1, sizeof(char)); - - if (text != NULL) - { - unsigned int count = (unsigned int)fread(text, sizeof(char), size, file); - - // WARNING: \r\n is converted to \n on reading, so, - // read bytes count gets reduced by the number of lines - if (count < size) text = (char *)RL_REALLOC(text, count + 1); - - // Zero-terminate the string - text[count] = '\0'; - - TRACELOG(LOG_INFO, "FILEIO: [%s] Text file loaded successfully", fileName); - } - else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName); - } - else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read text file", fileName); - - fclose(file); - } - else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName); -#else - TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback"); -#endif - } - else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); - - return text; -} - -// Unload file text data allocated by LoadFileText() -void UnloadFileText(char *text) -{ - RL_FREE(text); -} - -// Save text data to file (write), string must be '\0' terminated -bool SaveFileText(const char *fileName, const char *text) -{ - bool success = false; - - if (fileName != NULL) - { - if (saveFileText) - { - return saveFileText(fileName, text); - } -#if defined(SUPPORT_STANDARD_FILEIO) - FILE *file = fopen(fileName, "wt"); - - if (file != NULL) - { - int count = fprintf(file, "%s", text); - - if (count < 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName); - else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName); - - int result = fclose(file); - if (result == 0) success = true; - } - else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName); -#else - TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback"); -#endif - } - else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); - - return success; -} - -#if defined(PLATFORM_ANDROID) -// Initialize asset manager from android app -void InitAssetManager(AAssetManager *manager, const char *dataPath) -{ - assetManager = manager; - internalDataPath = dataPath; -} - -// Replacement for fopen() -// REF: https://developer.android.com/ndk/reference/group/asset -FILE *android_fopen(const char *fileName, const char *mode) -{ - FILE *file = NULL; - - if (mode[0] == 'w') - { - // NOTE: fopen() is mapped to android_fopen() that only grants read access to - // assets directory through AAssetManager but we want to also be able to - // write data when required using the standard stdio FILE access functions - // REF: https://stackoverflow.com/questions/11294487/android-writing-saving-files-from-native-code-only - #undef fopen - file = fopen(TextFormat("%s/%s", internalDataPath, fileName), mode); - #define fopen(name, mode) android_fopen(name, mode) - } - else - { - // NOTE: AAsset provides access to read-only asset - AAsset *asset = AAssetManager_open(assetManager, fileName, AASSET_MODE_UNKNOWN); - - if (asset != NULL) - { - // Get pointer to file in the assets - file = funopen(asset, android_read, android_write, android_seek, android_close); - } - else - { - #undef fopen - // Just do a regular open if file is not found in the assets - file = fopen(TextFormat("%s/%s", internalDataPath, fileName), mode); - if (file == NULL) file = fopen(fileName, mode); - #define fopen(name, mode) android_fopen(name, mode) - } - } - - return file; -} -#endif // PLATFORM_ANDROID - -//---------------------------------------------------------------------------------- -// Module Internal Functions Definition -//---------------------------------------------------------------------------------- -#if defined(PLATFORM_ANDROID) -static int android_read(void *cookie, char *data, int dataSize) -{ - return AAsset_read((AAsset *)cookie, data, dataSize); -} - -static int android_write(void *cookie, const char *data, int dataSize) -{ - TRACELOG(LOG_WARNING, "ANDROID: Failed to provide write access to APK"); - - return EACCES; -} - -static fpos_t android_seek(void *cookie, fpos_t offset, int whence) -{ - return AAsset_seek((AAsset *)cookie, offset, whence); -} - -static int android_close(void *cookie) -{ - AAsset_close((AAsset *)cookie); - return 0; -} -#endif // PLATFORM_ANDROID diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index 9c15ac285..000000000 --- a/src/utils.h +++ /dev/null @@ -1,74 +0,0 @@ -/********************************************************************************************** -* -* raylib.utils - Some common utility functions -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2014-2026 Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -#ifndef UTILS_H -#define UTILS_H - -#if defined(PLATFORM_ANDROID) - #include // Required for: FILE - #include // Required for: AAssetManager -#endif - -#if defined(SUPPORT_TRACELOG) - #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) -#else - #define TRACELOG(level, ...) (void)0 -#endif - -//---------------------------------------------------------------------------------- -// Some basic Defines -//---------------------------------------------------------------------------------- -#if defined(PLATFORM_ANDROID) - #define fopen(name, mode) android_fopen(name, mode) -#endif - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -// Nop... - -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- -#if defined(__cplusplus) -extern "C" { // Prevents name mangling of functions -#endif - -#if defined(PLATFORM_ANDROID) -void InitAssetManager(AAssetManager *manager, const char *dataPath); // Initialize asset manager from android app -FILE *android_fopen(const char *fileName, const char *mode); // Replacement for fopen() -> Read-only! -#endif - -#if defined(__cplusplus) -} -#endif - -#endif // UTILS_H From 21f026a48492a649f74a53519f82809cd5ba0119 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 10 Jan 2026 11:13:29 +0000 Subject: [PATCH 102/117] rlparser: update raylib_api.* by CI --- tools/rlparser/output/raylib_api.json | 118 +++++++++++++------------- tools/rlparser/output/raylib_api.lua | 86 +++++++++---------- tools/rlparser/output/raylib_api.txt | 80 ++++++++--------- tools/rlparser/output/raylib_api.xml | 34 ++++---- 4 files changed, 159 insertions(+), 159 deletions(-) diff --git a/tools/rlparser/output/raylib_api.json b/tools/rlparser/output/raylib_api.json index 66d8e9f30..185516563 100644 --- a/tools/rlparser/output/raylib_api.json +++ b/tools/rlparser/output/raylib_api.json @@ -4191,6 +4191,17 @@ } ] }, + { + "name": "SetTraceLogLevel", + "description": "Set the current threshold (minimum) log level", + "returnType": "void", + "params": [ + { + "type": "int", + "name": "logLevel" + } + ] + }, { "name": "TraceLog", "description": "Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...)", @@ -4211,13 +4222,13 @@ ] }, { - "name": "SetTraceLogLevel", - "description": "Set the current threshold (minimum) log level", + "name": "SetTraceLogCallback", + "description": "Set custom trace log", "returnType": "void", "params": [ { - "type": "int", - "name": "logLevel" + "type": "TraceLogCallback", + "name": "callback" } ] }, @@ -4258,61 +4269,6 @@ } ] }, - { - "name": "SetTraceLogCallback", - "description": "Set custom trace log", - "returnType": "void", - "params": [ - { - "type": "TraceLogCallback", - "name": "callback" - } - ] - }, - { - "name": "SetLoadFileDataCallback", - "description": "Set custom file binary data loader", - "returnType": "void", - "params": [ - { - "type": "LoadFileDataCallback", - "name": "callback" - } - ] - }, - { - "name": "SetSaveFileDataCallback", - "description": "Set custom file binary data saver", - "returnType": "void", - "params": [ - { - "type": "SaveFileDataCallback", - "name": "callback" - } - ] - }, - { - "name": "SetLoadFileTextCallback", - "description": "Set custom file text data loader", - "returnType": "void", - "params": [ - { - "type": "LoadFileTextCallback", - "name": "callback" - } - ] - }, - { - "name": "SetSaveFileTextCallback", - "description": "Set custom file text data saver", - "returnType": "void", - "params": [ - { - "type": "SaveFileTextCallback", - "name": "callback" - } - ] - }, { "name": "LoadFileData", "description": "Load file data as byte array (read)", @@ -4414,6 +4370,50 @@ } ] }, + { + "name": "SetLoadFileDataCallback", + "description": "Set custom file binary data loader", + "returnType": "void", + "params": [ + { + "type": "LoadFileDataCallback", + "name": "callback" + } + ] + }, + { + "name": "SetSaveFileDataCallback", + "description": "Set custom file binary data saver", + "returnType": "void", + "params": [ + { + "type": "SaveFileDataCallback", + "name": "callback" + } + ] + }, + { + "name": "SetLoadFileTextCallback", + "description": "Set custom file text data loader", + "returnType": "void", + "params": [ + { + "type": "LoadFileTextCallback", + "name": "callback" + } + ] + }, + { + "name": "SetSaveFileTextCallback", + "description": "Set custom file text data saver", + "returnType": "void", + "params": [ + { + "type": "SaveFileTextCallback", + "name": "callback" + } + ] + }, { "name": "FileRename", "description": "Rename file (if exists)", diff --git a/tools/rlparser/output/raylib_api.lua b/tools/rlparser/output/raylib_api.lua index 192ad963a..f2836e1ff 100644 --- a/tools/rlparser/output/raylib_api.lua +++ b/tools/rlparser/output/raylib_api.lua @@ -3864,6 +3864,14 @@ return { {type = "const char *", name = "url"} } }, + { + name = "SetTraceLogLevel", + description = "Set the current threshold (minimum) log level", + returnType = "void", + params = { + {type = "int", name = "logLevel"} + } + }, { name = "TraceLog", description = "Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...)", @@ -3875,11 +3883,11 @@ return { } }, { - name = "SetTraceLogLevel", - description = "Set the current threshold (minimum) log level", + name = "SetTraceLogCallback", + description = "Set custom trace log", returnType = "void", params = { - {type = "int", name = "logLevel"} + {type = "TraceLogCallback", name = "callback"} } }, { @@ -3907,46 +3915,6 @@ return { {type = "void *", name = "ptr"} } }, - { - name = "SetTraceLogCallback", - description = "Set custom trace log", - returnType = "void", - params = { - {type = "TraceLogCallback", name = "callback"} - } - }, - { - name = "SetLoadFileDataCallback", - description = "Set custom file binary data loader", - returnType = "void", - params = { - {type = "LoadFileDataCallback", name = "callback"} - } - }, - { - name = "SetSaveFileDataCallback", - description = "Set custom file binary data saver", - returnType = "void", - params = { - {type = "SaveFileDataCallback", name = "callback"} - } - }, - { - name = "SetLoadFileTextCallback", - description = "Set custom file text data loader", - returnType = "void", - params = { - {type = "LoadFileTextCallback", name = "callback"} - } - }, - { - name = "SetSaveFileTextCallback", - description = "Set custom file text data saver", - returnType = "void", - params = { - {type = "SaveFileTextCallback", name = "callback"} - } - }, { name = "LoadFileData", description = "Load file data as byte array (read)", @@ -4009,6 +3977,38 @@ return { {type = "const char *", name = "text"} } }, + { + name = "SetLoadFileDataCallback", + description = "Set custom file binary data loader", + returnType = "void", + params = { + {type = "LoadFileDataCallback", name = "callback"} + } + }, + { + name = "SetSaveFileDataCallback", + description = "Set custom file binary data saver", + returnType = "void", + params = { + {type = "SaveFileDataCallback", name = "callback"} + } + }, + { + name = "SetLoadFileTextCallback", + description = "Set custom file text data loader", + returnType = "void", + params = { + {type = "LoadFileTextCallback", name = "callback"} + } + }, + { + name = "SetSaveFileTextCallback", + description = "Set custom file text data saver", + returnType = "void", + params = { + {type = "SaveFileTextCallback", name = "callback"} + } + }, { name = "FileRename", description = "Rename file (if exists)", diff --git a/tools/rlparser/output/raylib_api.txt b/tools/rlparser/output/raylib_api.txt index f60f8fc81..0676b8138 100644 --- a/tools/rlparser/output/raylib_api.txt +++ b/tools/rlparser/output/raylib_api.txt @@ -1563,100 +1563,100 @@ Function 106: OpenURL() (1 input parameters) Return type: void Description: Open URL with default system browser (if available) Param[1]: url (type: const char *) -Function 107: TraceLog() (3 input parameters) +Function 107: SetTraceLogLevel() (1 input parameters) + Name: SetTraceLogLevel + Return type: void + Description: Set the current threshold (minimum) log level + Param[1]: logLevel (type: int) +Function 108: TraceLog() (3 input parameters) Name: TraceLog Return type: void Description: Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) Param[1]: logLevel (type: int) Param[2]: text (type: const char *) Param[3]: args (type: ...) -Function 108: SetTraceLogLevel() (1 input parameters) - Name: SetTraceLogLevel +Function 109: SetTraceLogCallback() (1 input parameters) + Name: SetTraceLogCallback Return type: void - Description: Set the current threshold (minimum) log level - Param[1]: logLevel (type: int) -Function 109: MemAlloc() (1 input parameters) + Description: Set custom trace log + Param[1]: callback (type: TraceLogCallback) +Function 110: MemAlloc() (1 input parameters) Name: MemAlloc Return type: void * Description: Internal memory allocator Param[1]: size (type: unsigned int) -Function 110: MemRealloc() (2 input parameters) +Function 111: MemRealloc() (2 input parameters) Name: MemRealloc Return type: void * Description: Internal memory reallocator Param[1]: ptr (type: void *) Param[2]: size (type: unsigned int) -Function 111: MemFree() (1 input parameters) +Function 112: MemFree() (1 input parameters) Name: MemFree Return type: void Description: Internal memory free Param[1]: ptr (type: void *) -Function 112: SetTraceLogCallback() (1 input parameters) - Name: SetTraceLogCallback - Return type: void - Description: Set custom trace log - Param[1]: callback (type: TraceLogCallback) -Function 113: SetLoadFileDataCallback() (1 input parameters) - Name: SetLoadFileDataCallback - Return type: void - Description: Set custom file binary data loader - Param[1]: callback (type: LoadFileDataCallback) -Function 114: SetSaveFileDataCallback() (1 input parameters) - Name: SetSaveFileDataCallback - Return type: void - Description: Set custom file binary data saver - Param[1]: callback (type: SaveFileDataCallback) -Function 115: SetLoadFileTextCallback() (1 input parameters) - Name: SetLoadFileTextCallback - Return type: void - Description: Set custom file text data loader - Param[1]: callback (type: LoadFileTextCallback) -Function 116: SetSaveFileTextCallback() (1 input parameters) - Name: SetSaveFileTextCallback - Return type: void - Description: Set custom file text data saver - Param[1]: callback (type: SaveFileTextCallback) -Function 117: LoadFileData() (2 input parameters) +Function 113: LoadFileData() (2 input parameters) Name: LoadFileData Return type: unsigned char * Description: Load file data as byte array (read) Param[1]: fileName (type: const char *) Param[2]: dataSize (type: int *) -Function 118: UnloadFileData() (1 input parameters) +Function 114: UnloadFileData() (1 input parameters) Name: UnloadFileData Return type: void Description: Unload file data allocated by LoadFileData() Param[1]: data (type: unsigned char *) -Function 119: SaveFileData() (3 input parameters) +Function 115: SaveFileData() (3 input parameters) Name: SaveFileData Return type: bool Description: Save data to file from byte array (write), returns true on success Param[1]: fileName (type: const char *) Param[2]: data (type: void *) Param[3]: dataSize (type: int) -Function 120: ExportDataAsCode() (3 input parameters) +Function 116: ExportDataAsCode() (3 input parameters) Name: ExportDataAsCode Return type: bool Description: Export data to code (.h), returns true on success Param[1]: data (type: const unsigned char *) Param[2]: dataSize (type: int) Param[3]: fileName (type: const char *) -Function 121: LoadFileText() (1 input parameters) +Function 117: LoadFileText() (1 input parameters) Name: LoadFileText Return type: char * Description: Load text data from file (read), returns a '\0' terminated string Param[1]: fileName (type: const char *) -Function 122: UnloadFileText() (1 input parameters) +Function 118: UnloadFileText() (1 input parameters) Name: UnloadFileText Return type: void Description: Unload file text data allocated by LoadFileText() Param[1]: text (type: char *) -Function 123: SaveFileText() (2 input parameters) +Function 119: SaveFileText() (2 input parameters) Name: SaveFileText Return type: bool Description: Save text data to file (write), string must be '\0' terminated, returns true on success Param[1]: fileName (type: const char *) Param[2]: text (type: const char *) +Function 120: SetLoadFileDataCallback() (1 input parameters) + Name: SetLoadFileDataCallback + Return type: void + Description: Set custom file binary data loader + Param[1]: callback (type: LoadFileDataCallback) +Function 121: SetSaveFileDataCallback() (1 input parameters) + Name: SetSaveFileDataCallback + Return type: void + Description: Set custom file binary data saver + Param[1]: callback (type: SaveFileDataCallback) +Function 122: SetLoadFileTextCallback() (1 input parameters) + Name: SetLoadFileTextCallback + Return type: void + Description: Set custom file text data loader + Param[1]: callback (type: LoadFileTextCallback) +Function 123: SetSaveFileTextCallback() (1 input parameters) + Name: SetSaveFileTextCallback + Return type: void + Description: Set custom file text data saver + Param[1]: callback (type: SaveFileTextCallback) Function 124: FileRename() (2 input parameters) Name: FileRename Return type: int diff --git a/tools/rlparser/output/raylib_api.xml b/tools/rlparser/output/raylib_api.xml index 1bbeb175c..3853ac74f 100644 --- a/tools/rlparser/output/raylib_api.xml +++ b/tools/rlparser/output/raylib_api.xml @@ -988,13 +988,16 @@ + + + - - + + @@ -1006,21 +1009,6 @@ - - - - - - - - - - - - - - - @@ -1048,6 +1036,18 @@ + + + + + + + + + + + + From 1606dca0cbece4ffc7769bf664a86e05b1866fbb Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 10 Jan 2026 12:20:26 +0100 Subject: [PATCH 103/117] Update CMakeLists.txt --- src/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 087141447..76c985969 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,7 +36,6 @@ set(raylib_sources rshapes.c rtext.c rtextures.c - utils.c ) # /cmake/GlfwImport.cmake handles the details around the inclusion of glfw From d94ea00a9782ce7f9dc451a0f9afc9fa313b6954 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 11 Jan 2026 00:27:24 +0100 Subject: [PATCH 104/117] Update raylib.sln --- projects/VS2022/raylib.sln | 2 ++ 1 file changed, 2 insertions(+) diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 9b088f52d..47e83ef4c 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -5661,6 +5661,8 @@ Global {1F4722E7-F78E-413F-A106-D3490211EA57} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {0A0FC982-6E31-401F-BA77-3C5E8AB02C68} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} {DC163251-16C3-4B72-B965-ACDBA0F02BD1} = {278D8859-20B1-428F-8448-064F46E1F021} + {D35D2FDA-B53F-4F70-81CA-24D95812B89C} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} From 972d6f0775f1d51199654dcc14e4285bec746770 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 11 Jan 2026 00:32:16 +0100 Subject: [PATCH 105/117] REXM: Fix raylib building --- tools/rexm/VS2022/raylib/raylib.vcxproj | 2 -- tools/rexm/VS2022/raylib/raylib.vcxproj.filters | 2 -- 2 files changed, 4 deletions(-) diff --git a/tools/rexm/VS2022/raylib/raylib.vcxproj b/tools/rexm/VS2022/raylib/raylib.vcxproj index df2831b33..dbc6272ed 100644 --- a/tools/rexm/VS2022/raylib/raylib.vcxproj +++ b/tools/rexm/VS2022/raylib/raylib.vcxproj @@ -312,7 +312,6 @@ - @@ -320,7 +319,6 @@ - diff --git a/tools/rexm/VS2022/raylib/raylib.vcxproj.filters b/tools/rexm/VS2022/raylib/raylib.vcxproj.filters index b5f5536dc..5914ba240 100644 --- a/tools/rexm/VS2022/raylib/raylib.vcxproj.filters +++ b/tools/rexm/VS2022/raylib/raylib.vcxproj.filters @@ -8,7 +8,6 @@ - @@ -16,7 +15,6 @@ - external From 483b26ef843cff0cda5f939033c9bc4bca73d879 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 11 Jan 2026 01:04:32 +0100 Subject: [PATCH 106/117] Update rexm.c --- tools/rexm/rexm.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 95ed13739..ad9bcbed0 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -660,7 +660,8 @@ int main(int argc, char *argv[]) // we must store provided file paths because pointers will be overwriten // TODO: It seems projects are added to solution BUT not to required solution folder, // that process still requires to be done manually - LOG("INFO: [%s] Adding project to raylib solution (.sln)\n", TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName)); + LOG("INFO: [%s] Adding project to raylib solution (.sln)\n", + TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName)); AddVSProjectToSolution(exVSProjectSolutionFile, TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), exCategory); //------------------------------------------------------------------------------------------------ @@ -2613,7 +2614,7 @@ static int AddVSProjectToSolution(const char *slnFile, const char *projFile, con int result = 0; // WARNING: Function uses extensively TextFormat(), - // *projFile ptr will be overwriten after a while + // *projFile ptr could be overwriten after a while -> Use copied string // Generate unique UUID const char *uuid = GenerateUUIDv4(); @@ -2697,14 +2698,22 @@ static int AddVSProjectToSolution(const char *slnFile, const char *projFile, con // Add project folder line // NOTE: Folder uuid depends on category - if (strcmp(category, "core") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035}\n", uuid)); - else if (strcmp(category, "shapes") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s} = {278D8859-20B1-428F-8448-064F46E1F021}\n", uuid)); - else if (strcmp(category, "textures") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE}\n", uuid)); - else if (strcmp(category, "text") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A}\n", uuid)); - else if (strcmp(category, "models") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C}\n", uuid)); - else if (strcmp(category, "shaders") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9}\n", uuid)); - else if (strcmp(category, "audio") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s} = {CC132A4D-D081-4C26-BFB9-AB11984054F8}\n", uuid)); - else if (strcmp(category, "other") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s} = {E9D708A5-9C1F-4B84-A795-C5F191801762}\n", uuid)); + if (strcmp(category, "core") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, + TextFormat("\t\t{%s} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035}\n", uuid)); + else if (strcmp(category, "shapes") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, + TextFormat("\t\t{%s} = {278D8859-20B1-428F-8448-064F46E1F021}\n", uuid)); + else if (strcmp(category, "textures") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, + TextFormat("\t\t{%s} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE}\n", uuid)); + else if (strcmp(category, "text") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, + TextFormat("\t\t{%s} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A}\n", uuid)); + else if (strcmp(category, "models") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, + TextFormat("\t\t{%s} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C}\n", uuid)); + else if (strcmp(category, "shaders") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, + TextFormat("\t\t{%s} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9}\n", uuid)); + else if (strcmp(category, "audio") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, + TextFormat("\t\t{%s} = {CC132A4D-D081-4C26-BFB9-AB11984054F8}\n", uuid)); + else if (strcmp(category, "other") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, + TextFormat("\t\t{%s} = {E9D708A5-9C1F-4B84-A795-C5F191801762}\n", uuid)); else LOG("WARNING: Provided category is not valid: %s\n", category); //---------------------------------------------------------------------------------------- From 51bdaa34fa2f84063d643d999e44f54d52cc38d1 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 11 Jan 2026 16:56:47 +0100 Subject: [PATCH 107/117] Update raylib.vcxproj --- projects/VS2022/raylib/raylib.vcxproj | 1 - 1 file changed, 1 deletion(-) diff --git a/projects/VS2022/raylib/raylib.vcxproj b/projects/VS2022/raylib/raylib.vcxproj index 287410f06..8cc3fae7a 100644 --- a/projects/VS2022/raylib/raylib.vcxproj +++ b/projects/VS2022/raylib/raylib.vcxproj @@ -601,7 +601,6 @@ - From 4badbe2b1751b88a813c5f2cfe7ac94d2a979078 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 11 Jan 2026 21:02:59 +0100 Subject: [PATCH 108/117] REVIEWED: Variable scope #5485 --- src/platforms/rcore_drm.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 2e224ad92..a0ae1fa37 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -1415,6 +1415,7 @@ int InitPlatform(void) }; EGLint numConfigs = 0; + const char *eglClientExtensions = NULL; // Get an EGL device connection // NOTE: eglGetPlatformDisplay() is preferred over eglGetDisplay() legacy call @@ -1424,14 +1425,12 @@ int InitPlatform(void) #else // Check if extension is available for eglGetPlatformDisplayEXT() // NOTE: Better compatibility with some drivers (e.g. Mali Midgard) - const char *eglClientExtensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); - if (eglClientExtensions != NULL) + eglClientExtensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + if ((eglClientExtensions != NULL) && (strstr(eglClientExtensions, "EGL_EXT_platform_base") != NULL)) { - if (strstr(eglClientExtensions, "EGL_EXT_platform_base") != NULL) - { - PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); - if (eglGetPlatformDisplayEXT != NULL) platform.device = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, platform.gbmDevice, NULL); - } + PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); + + if (eglGetPlatformDisplayEXT != NULL) platform.device = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, platform.gbmDevice, NULL); } // In case extension not found or display could not be retrieved, try useing legacy version @@ -1520,13 +1519,9 @@ int InitPlatform(void) if ((eglClientExtensions != NULL) && (strstr(eglClientExtensions, "EGL_EXT_platform_base") != NULL)) { - PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT = - (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT"); + PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT = (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT"); - if (eglCreatePlatformWindowSurfaceEXT != NULL) - { - platform.surface = eglCreatePlatformWindowSurfaceEXT(platform.device, platform.config, platform.gbmSurface, NULL); - } + if (eglCreatePlatformWindowSurfaceEXT != NULL) platform.surface = eglCreatePlatformWindowSurfaceEXT(platform.device, platform.config, platform.gbmSurface, NULL); } if (platform.surface == EGL_NO_SURFACE) From d7c38cfbe43dc9f053eacb219cf9523e9c2372b0 Mon Sep 17 00:00:00 2001 From: James Mintram Date: Sun, 11 Jan 2026 21:01:14 +0000 Subject: [PATCH 109/117] fix rglfw.c path in build.zig for bsd platforms (#5486) --- build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 2e53bfbdc..ab98bbe98 100644 --- a/build.zig +++ b/build.zig @@ -348,7 +348,7 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. } }, .freebsd, .openbsd, .netbsd, .dragonfly => { - try c_source_files.append(b.allocator, "rglfw.c"); + try c_source_files.append(b.allocator, "src/rglfw.c"); raylib.root_module.linkSystemLibrary("GL", .{}); raylib.root_module.linkSystemLibrary("rt", .{}); raylib.root_module.linkSystemLibrary("dl", .{}); From 32e7732061ec425aa261e7eede3b483fa33adbef Mon Sep 17 00:00:00 2001 From: Morgan Moore Date: Sun, 11 Jan 2026 17:43:38 -0500 Subject: [PATCH 110/117] Update build.zig to work with 0.16 (#5487) --- build.zig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/build.zig b/build.zig index ab98bbe98..111a1bcb1 100644 --- a/build.zig +++ b/build.zig @@ -522,12 +522,13 @@ fn addExamples( raylib: *std.Build.Step.Compile, ) !*std.Build.Step { const all = b.step(module, "All " ++ module ++ " examples"); + const io = all.owner.graph.io; const module_subpath = b.pathJoin(&.{ "examples", module }); - var dir = try std.fs.cwd().openDir(b.pathFromRoot(module_subpath), .{ .iterate = true }); - defer dir.close(); + var dir = try std.Io.Dir.cwd().openDir(io, b.pathFromRoot(module_subpath), .{ .iterate = true }); + defer dir.close(io); var iter = dir.iterate(); - while (try iter.next()) |entry| { + while (try iter.next(io)) |entry| { if (entry.kind != .file) continue; const extension_idx = std.mem.lastIndexOf(u8, entry.name, ".c") orelse continue; const name = entry.name[0..extension_idx]; From 0c33c603f4d7244b652fd6d133fa8a1ed8ae583f Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 12 Jan 2026 13:04:38 +0100 Subject: [PATCH 111/117] REVIEWED: `EXTERNAL_CONFIG_FLAGS` usage, check moved to `config.h` Due to `utils` module removal, `EXTERNAL_CONFIG_FLAGS` was not working, so the system was redesigned. This change is independent of #4411 --- src/config.h | 40 +++++++++++++++++++++++++++------------- src/raudio.c | 6 +----- src/rcore.c | 5 +---- src/rmodels.c | 5 +---- src/rshapes.c | 5 +---- src/rtext.c | 5 +---- src/rtextures.c | 5 +---- 7 files changed, 33 insertions(+), 38 deletions(-) diff --git a/src/config.h b/src/config.h index 68b42cc0e..5e7c6f1d2 100644 --- a/src/config.h +++ b/src/config.h @@ -32,25 +32,22 @@ // Module selection - Some modules could be avoided // Mandatory modules: rcore, rlgl //------------------------------------------------------------------------------------ -#define SUPPORT_MODULE_RSHAPES 1 -#define SUPPORT_MODULE_RTEXTURES 1 -#define SUPPORT_MODULE_RTEXT 1 // WARNING: It requires SUPPORT_MODULE_RTEXTURES to load sprite font textures -#define SUPPORT_MODULE_RMODELS 1 -#define SUPPORT_MODULE_RAUDIO 1 +#if !defined(EXTERNAL_CONFIG_FLAGS) + #define SUPPORT_MODULE_RSHAPES 1 + #define SUPPORT_MODULE_RTEXTURES 1 + #define SUPPORT_MODULE_RTEXT 1 // WARNING: It requires SUPPORT_MODULE_RTEXTURES to load sprite font textures + #define SUPPORT_MODULE_RMODELS 1 + #define SUPPORT_MODULE_RAUDIO 1 +#endif //------------------------------------------------------------------------------------ // Module: rcore - Configuration Flags //------------------------------------------------------------------------------------ +#if !defined(EXTERNAL_CONFIG_FLAGS) // Standard file io library (stdio.h) included #define SUPPORT_STANDARD_FILEIO 1 // Show TRACELOG() output messages #define SUPPORT_TRACELOG 1 -#if defined(SUPPORT_TRACELOG) - #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) -#else - #define TRACELOG(level, ...) (void)0 -#endif - // Camera module is included (rcamera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital #define SUPPORT_CAMERA_SYSTEM 1 // Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag @@ -79,10 +76,10 @@ // By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents() // Enabling this flag allows manual control of the frame processes, use at your own risk //#define SUPPORT_CUSTOM_FRAME_CONTROL 1 - // Support for clipboard image loading // NOTE: Only working on SDL3, GLFW (Windows) and RGFW (Windows) #define SUPPORT_CLIPBOARD_IMAGE 1 +#endif // NOTE: Clipboard image loading requires support for some image file formats // TODO: Those defines should probably be removed from here, letting the user manage them @@ -104,6 +101,12 @@ #endif #endif +#if defined(SUPPORT_TRACELOG) + #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) +#else + #define TRACELOG(level, ...) (void)0 +#endif + // rcore: Configuration values //------------------------------------------------------------------------------------ #define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message @@ -127,7 +130,7 @@ //------------------------------------------------------------------------------------ // Module: rlgl - Configuration values //------------------------------------------------------------------------------------ - +#if !defined(EXTERNAL_CONFIG_FLAGS) // Enable OpenGL Debug Context (only available on OpenGL 4.3) //#define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT 1 @@ -135,6 +138,7 @@ //#define RLGL_SHOW_GL_DETAILS_INFO 1 #define RL_SUPPORT_MESH_GPU_SKINNING 1 // GPU skinning, comment if your GPU does not support more than 8 VBOs +#endif //#define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 4096 // Default internal render batch elements limits #define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering) @@ -184,9 +188,11 @@ //------------------------------------------------------------------------------------ // Module: rshapes - Configuration Flags //------------------------------------------------------------------------------------ +#if !defined(EXTERNAL_CONFIG_FLAGS) // Use QUADS instead of TRIANGLES for drawing when possible // Some lines-based shapes could still use lines #define SUPPORT_QUADS_DRAW_MODE 1 +#endif // rshapes: Configuration values //------------------------------------------------------------------------------------ @@ -195,6 +201,7 @@ //------------------------------------------------------------------------------------ // Module: rtextures - Configuration Flags //------------------------------------------------------------------------------------ +#if !defined(EXTERNAL_CONFIG_FLAGS) // Selected desired fileformats to be supported for image data loading #define SUPPORT_FILEFORMAT_PNG 1 //#define SUPPORT_FILEFORMAT_BMP 1 @@ -218,10 +225,12 @@ // Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... // If not defined, still some functions are supported: ImageFormat(), ImageCrop(), ImageToPOT() #define SUPPORT_IMAGE_MANIPULATION 1 +#endif //------------------------------------------------------------------------------------ // Module: rtext - Configuration Flags //------------------------------------------------------------------------------------ +#if !defined(EXTERNAL_CONFIG_FLAGS) // Default font is loaded on window initialization to be available for the user to render simple text // NOTE: If enabled, uses external module functions to load default raylib font #define SUPPORT_DEFAULT_FONT 1 @@ -241,6 +250,7 @@ // Support conservative font atlas size estimation //#define SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE 1 +#endif // rtext: Configuration values //------------------------------------------------------------------------------------ @@ -251,6 +261,7 @@ //------------------------------------------------------------------------------------ // Module: rmodels - Configuration Flags //------------------------------------------------------------------------------------ +#if !defined(EXTERNAL_CONFIG_FLAGS) // Selected desired model fileformats to be supported for loading #define SUPPORT_FILEFORMAT_OBJ 1 #define SUPPORT_FILEFORMAT_MTL 1 @@ -261,6 +272,7 @@ // Support procedural mesh generation functions, uses external par_shapes.h library // NOTE: Some generated meshes DO NOT include generated texture coordinates #define SUPPORT_MESH_GENERATION 1 +#endif // rmodels: Configuration values //------------------------------------------------------------------------------------ @@ -275,6 +287,7 @@ //------------------------------------------------------------------------------------ // Module: raudio - Configuration Flags //------------------------------------------------------------------------------------ +#if !defined(EXTERNAL_CONFIG_FLAGS) // Desired audio fileformats to be supported for loading #define SUPPORT_FILEFORMAT_WAV 1 #define SUPPORT_FILEFORMAT_OGG 1 @@ -283,6 +296,7 @@ //#define SUPPORT_FILEFORMAT_FLAC 1 #define SUPPORT_FILEFORMAT_XM 1 #define SUPPORT_FILEFORMAT_MOD 1 +#endif // raudio: Configuration values //------------------------------------------------------------------------------------ diff --git a/src/raudio.c b/src/raudio.c index c25ad1f02..18f9e0aad 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -74,11 +74,7 @@ #else #include "raylib.h" // Declares module functions - // Check if config flags have been externally provided on compilation line - #if !defined(EXTERNAL_CONFIG_FLAGS) - #include "config.h" // Defines module configuration flags - #endif - //#include "utils.h" // Required for: fopen() Android mapping + #include "config.h" // Defines module configuration flags #endif #if defined(SUPPORT_MODULE_RAUDIO) || defined(RAUDIO_STANDALONE) diff --git a/src/rcore.c b/src/rcore.c index 7837f8c4e..dd4123a3b 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -104,10 +104,7 @@ #include "raylib.h" // Declares module functions -// Check if config flags have been externally provided on compilation line -#if !defined(EXTERNAL_CONFIG_FLAGS) - #include "config.h" // Defines module configuration flags -#endif +#include "config.h" // Defines module configuration flags #include // Required for: srand(), rand(), atexit(), exit() #include // Required for: FILE, fopen(), fseek(), ftell(), fread(), fwrite(), fprintf(), vprintf(), fclose(), sprintf() [Used in OpenURL()] diff --git a/src/rmodels.c b/src/rmodels.c index 76efa3e4a..58b08350d 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -42,10 +42,7 @@ #include "raylib.h" // Declares module functions -// Check if config flags have been externally provided on compilation line -#if !defined(EXTERNAL_CONFIG_FLAGS) - #include "config.h" // Defines module configuration flags -#endif +#include "config.h" // Defines module configuration flags #if defined(SUPPORT_MODULE_RMODELS) diff --git a/src/rshapes.c b/src/rshapes.c index e828b98bc..3f686f21a 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -46,10 +46,7 @@ #include "raylib.h" // Declares module functions -// Check if config flags have been externally provided on compilation line -#if !defined(EXTERNAL_CONFIG_FLAGS) - #include "config.h" // Defines module configuration flags -#endif +#include "config.h" // Defines module configuration flags #if defined(SUPPORT_MODULE_RSHAPES) diff --git a/src/rtext.c b/src/rtext.c index 7a1b3c027..8085e81c8 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -55,10 +55,7 @@ #include "raylib.h" // Declares module functions -// Check if config flags have been externally provided on compilation line -#if !defined(EXTERNAL_CONFIG_FLAGS) - #include "config.h" // Defines module configuration flags -#endif +#include "config.h" // Defines module configuration flags #if defined(SUPPORT_MODULE_RTEXT) diff --git a/src/rtextures.c b/src/rtextures.c index fa03d193b..a20b5e516 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -63,10 +63,7 @@ #include "raylib.h" // Declares module functions -// Check if config flags have been externally provided on compilation line -#if !defined(EXTERNAL_CONFIG_FLAGS) - #include "config.h" // Defines module configuration flags -#endif +#include "config.h" // Defines module configuration flags #if defined(SUPPORT_MODULE_RTEXTURES) From 28b9411e9d8d14ba2bf991a5d2ce95f3498d0cde Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 12 Jan 2026 13:23:27 +0100 Subject: [PATCH 112/117] REMOVED: `RLGL_RENDER_TEXTURES_HINT`, enabled by default and no complaints of anyone having issues #5479 --- src/rlgl.h | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 7fa22f9cc..8b264343a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -37,10 +37,6 @@ * If not defined, the library is in header only mode and can be included in other headers * or source files without problems. But only ONE file should hold the implementation * -* #define RLGL_RENDER_TEXTURES_HINT -* Enable framebuffer objects (fbo) support (enabled by default) -* Some GPUs could not support them despite the OpenGL version -* * #define RLGL_SHOW_GL_DETAILS_INFO * Show OpenGL extensions and capabilities detailed logs on init * @@ -196,10 +192,6 @@ #define GRAPHICS_API_OPENGL_ES2 #endif -// Support framebuffer objects by default -// NOTE: Some driver implementation do not support it, despite they should -#define RLGL_RENDER_TEXTURES_HINT - //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -1863,7 +1855,7 @@ void rlDisableShader(void) // Enable rendering to texture (fbo) void rlEnableFramebuffer(unsigned int id) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) glBindFramebuffer(GL_FRAMEBUFFER, id); #endif } @@ -1872,7 +1864,7 @@ void rlEnableFramebuffer(unsigned int id) unsigned int rlGetActiveFramebuffer(void) { GLint fboId = 0; -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) && defined(RLGL_RENDER_TEXTURES_HINT) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fboId); #endif return fboId; @@ -1881,7 +1873,7 @@ unsigned int rlGetActiveFramebuffer(void) // Disable rendering to texture void rlDisableFramebuffer(void) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) glBindFramebuffer(GL_FRAMEBUFFER, 0); #endif } @@ -1889,7 +1881,7 @@ void rlDisableFramebuffer(void) // Blit active framebuffer to main framebuffer void rlBlitFramebuffer(int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight, int bufferMask) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) && defined(RLGL_RENDER_TEXTURES_HINT) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) glBlitFramebuffer(srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight, bufferMask, GL_NEAREST); #endif } @@ -1897,7 +1889,7 @@ void rlBlitFramebuffer(int srcX, int srcY, int srcWidth, int srcHeight, int dstX // Bind framebuffer object (fbo) void rlBindFramebuffer(unsigned int target, unsigned int framebuffer) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) glBindFramebuffer(target, framebuffer); #endif } @@ -1906,7 +1898,7 @@ void rlBindFramebuffer(unsigned int target, unsigned int framebuffer) // NOTE: One color buffer is always active by default void rlActiveDrawBuffers(int count) { -#if ((defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) && defined(RLGL_RENDER_TEXTURES_HINT)) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) // NOTE: Maximum number of draw buffers supported is implementation dependant, // it can be queried with glGet*() but it must be at least 8 //GLint maxDrawBuffers = 0; @@ -3826,7 +3818,7 @@ unsigned int rlLoadFramebuffer(void) unsigned int fboId = 0; if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return fboId; } -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) glGenFramebuffers(1, &fboId); // Create the framebuffer object glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind any framebuffer #endif @@ -3838,7 +3830,7 @@ unsigned int rlLoadFramebuffer(void) // NOTE: Attach type: 0-Color, 1-Depth renderbuffer, 2-Depth texture void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) glBindFramebuffer(GL_FRAMEBUFFER, fboId); switch (attachType) @@ -3878,7 +3870,7 @@ bool rlFramebufferComplete(unsigned int id) { bool result = false; -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) glBindFramebuffer(GL_FRAMEBUFFER, id); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -3909,7 +3901,7 @@ bool rlFramebufferComplete(unsigned int id) // NOTE: All attached textures/cubemaps/renderbuffers are also deleted void rlUnloadFramebuffer(unsigned int id) { -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) // Query depth attachment to automatically delete texture/renderbuffer int depthType = 0, depthId = 0; glBindFramebuffer(GL_FRAMEBUFFER, id); // Bind framebuffer to query depth texture type From 644ff28f87055e84289684036b67bc7149e38ac3 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 12 Jan 2026 13:36:40 +0100 Subject: [PATCH 113/117] Update shaders_deferred_rendering.c --- examples/shaders/shaders_deferred_rendering.c | 110 ++++++++---------- 1 file changed, 51 insertions(+), 59 deletions(-) diff --git a/examples/shaders/shaders_deferred_rendering.c b/examples/shaders/shaders_deferred_rendering.c index 811566917..4b03b69a4 100644 --- a/examples/shaders/shaders_deferred_rendering.c +++ b/examples/shaders/shaders_deferred_rendering.c @@ -40,13 +40,13 @@ //---------------------------------------------------------------------------------- // GBuffer data typedef struct GBuffer { - unsigned int framebuffer; + unsigned int framebufferId; - unsigned int positionTexture; - unsigned int normalTexture; - unsigned int albedoSpecTexture; + unsigned int positionTextureId; + unsigned int normalTextureId; + unsigned int albedoSpecTextureId; - unsigned int depthRenderbuffer; + unsigned int depthRenderbufferId; } GBuffer; // Deferred mode passes @@ -90,15 +90,10 @@ int main(void) // Initialize the G-buffer GBuffer gBuffer = { 0 }; - gBuffer.framebuffer = rlLoadFramebuffer(); + gBuffer.framebufferId = rlLoadFramebuffer(); + if (gBuffer.framebufferId == 0) TraceLog(LOG_WARNING, "Failed to create framebufferId"); - if (!gBuffer.framebuffer) - { - TraceLog(LOG_WARNING, "Failed to create framebuffer"); - exit(1); - } - - rlEnableFramebuffer(gBuffer.framebuffer); + rlEnableFramebuffer(gBuffer.framebufferId); // NOTE: Vertex positions are stored in a texture for simplicity. A better approach would use a depth texture // (instead of a detph renderbuffer) to reconstruct world positions in the final render shader via clip-space position, @@ -107,46 +102,42 @@ int main(void) // 16-bit precision ensures OpenGL ES 3 compatibility, though it may lack precision for real scenarios // But as mentioned above, the positions could be reconstructed instead of stored. If not targeting OpenGL ES // and you wish to maintain this approach, consider using `RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32` - gBuffer.positionTexture = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16, 1); + gBuffer.positionTextureId = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16, 1); // Similarly, 16-bit precision is used for normals ensures OpenGL ES 3 compatibility // This is generally sufficient, but a 16-bit fixed-point format offer a better uniform precision in all orientations - gBuffer.normalTexture = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16, 1); + gBuffer.normalTextureId = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16, 1); // Albedo (diffuse color) and specular strength can be combined into one texture // The color in RGB, and the specular strength in the alpha channel - gBuffer.albedoSpecTexture = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1); + gBuffer.albedoSpecTextureId = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1); - // Activate the draw buffers for our framebuffer + // Activate the draw buffers for our framebufferId rlActiveDrawBuffers(3); - // Now we attach our textures to the framebuffer - rlFramebufferAttach(gBuffer.framebuffer, gBuffer.positionTexture, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0); - rlFramebufferAttach(gBuffer.framebuffer, gBuffer.normalTexture, RL_ATTACHMENT_COLOR_CHANNEL1, RL_ATTACHMENT_TEXTURE2D, 0); - rlFramebufferAttach(gBuffer.framebuffer, gBuffer.albedoSpecTexture, RL_ATTACHMENT_COLOR_CHANNEL2, RL_ATTACHMENT_TEXTURE2D, 0); + // Now we attach our textures to the framebufferId + rlFramebufferAttach(gBuffer.framebufferId, gBuffer.positionTextureId, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0); + rlFramebufferAttach(gBuffer.framebufferId, gBuffer.normalTextureId, RL_ATTACHMENT_COLOR_CHANNEL1, RL_ATTACHMENT_TEXTURE2D, 0); + rlFramebufferAttach(gBuffer.framebufferId, gBuffer.albedoSpecTextureId, RL_ATTACHMENT_COLOR_CHANNEL2, RL_ATTACHMENT_TEXTURE2D, 0); // Finally we attach the depth buffer - gBuffer.depthRenderbuffer = rlLoadTextureDepth(screenWidth, screenHeight, true); - rlFramebufferAttach(gBuffer.framebuffer, gBuffer.depthRenderbuffer, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0); + gBuffer.depthRenderbufferId = rlLoadTextureDepth(screenWidth, screenHeight, true); + rlFramebufferAttach(gBuffer.framebufferId, gBuffer.depthRenderbufferId, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0); - // Make sure our framebuffer is complete - // NOTE: rlFramebufferComplete() automatically unbinds the framebuffer, so we don't have - // to rlDisableFramebuffer() here - if (!rlFramebufferComplete(gBuffer.framebuffer)) - { - TraceLog(LOG_WARNING, "Framebuffer is not complete"); - } + // Make sure our framebufferId is complete + // NOTE: rlFramebufferComplete() automatically unbinds the framebufferId, so we don't have to rlDisableFramebuffer() here + if (!rlFramebufferComplete(gBuffer.framebufferId)) TraceLog(LOG_WARNING, "Framebuffer is not complete"); // Now we initialize the sampler2D uniform's in the deferred shader // We do this by setting the uniform's values to the texture units that // we later bind our g-buffer textures to rlEnableShader(deferredShader.id); - int texUnitPosition = 0; - int texUnitNormal = 1; - int texUnitAlbedoSpec = 2; - SetShaderValue(deferredShader, rlGetLocationUniform(deferredShader.id, "gPosition"), &texUnitPosition, RL_SHADER_UNIFORM_SAMPLER2D); - SetShaderValue(deferredShader, rlGetLocationUniform(deferredShader.id, "gNormal"), &texUnitNormal, RL_SHADER_UNIFORM_SAMPLER2D); - SetShaderValue(deferredShader, rlGetLocationUniform(deferredShader.id, "gAlbedoSpec"), &texUnitAlbedoSpec, RL_SHADER_UNIFORM_SAMPLER2D); + int texUnitPosition = 0; + int texUnitNormal = 1; + int texUnitAlbedoSpec = 2; + SetShaderValue(deferredShader, rlGetLocationUniform(deferredShader.id, "gPosition"), &texUnitPosition, RL_SHADER_UNIFORM_SAMPLER2D); + SetShaderValue(deferredShader, rlGetLocationUniform(deferredShader.id, "gNormal"), &texUnitNormal, RL_SHADER_UNIFORM_SAMPLER2D); + SetShaderValue(deferredShader, rlGetLocationUniform(deferredShader.id, "gAlbedoSpec"), &texUnitAlbedoSpec, RL_SHADER_UNIFORM_SAMPLER2D); rlDisableShader(); // Assign out lighting shader to model @@ -176,7 +167,7 @@ int main(void) cubeRotations[i] = (float)(rand()%360); } - DeferredMode mode = DEFERRED_SHADING; + int mode = DEFERRED_SHADING; rlEnableDepthTest(); @@ -215,17 +206,16 @@ int main(void) BeginDrawing(); // Draw to the geometry buffer by first activating it - rlEnableFramebuffer(gBuffer.framebuffer); + rlEnableFramebuffer(gBuffer.framebufferId); rlClearColor(0, 0, 0, 0); rlClearScreenBuffers(); // Clear color and depth buffer - rlDisableColorBlend(); + BeginMode3D(camera); // NOTE: We have to use rlEnableShader here. `BeginShaderMode` or thus `rlSetShader` // will not work, as they won't immediately load the shader program rlEnableShader(gbufferShader.id); - // When drawing a model here, make sure that the material's shaders - // are set to the gbuffer shader! + // When drawing a model here, make sure that the material's shaders are set to the gbuffer shader! DrawModel(model, Vector3Zero(), 1.0f, WHITE); DrawModel(cube, (Vector3) { 0.0, 1.0f, 0.0 }, 1.0f, WHITE); @@ -234,12 +224,12 @@ int main(void) Vector3 position = cubePositions[i]; DrawModelEx(cube, position, (Vector3) { 1, 1, 1 }, cubeRotations[i], (Vector3) { CUBE_SCALE, CUBE_SCALE, CUBE_SCALE }, WHITE); } - rlDisableShader(); EndMode3D(); + rlEnableColorBlend(); - // Go back to the default framebuffer (0) and draw our deferred shading + // Go back to the default framebufferId (0) and draw our deferred shading rlDisableFramebuffer(); rlClearScreenBuffers(); // Clear color & depth buffer @@ -254,21 +244,21 @@ int main(void) // We are binding them to locations that we earlier set in sampler2D uniforms `gPosition`, `gNormal`, // and `gAlbedoSpec` rlActiveTextureSlot(texUnitPosition); - rlEnableTexture(gBuffer.positionTexture); + rlEnableTexture(gBuffer.positionTextureId); rlActiveTextureSlot(texUnitNormal); - rlEnableTexture(gBuffer.normalTexture); + rlEnableTexture(gBuffer.normalTextureId); rlActiveTextureSlot(texUnitAlbedoSpec); - rlEnableTexture(gBuffer.albedoSpecTexture); + rlEnableTexture(gBuffer.albedoSpecTextureId); - // Finally, we draw a fullscreen quad to our default framebuffer + // Finally, we draw a fullscreen quad to our default framebufferId // This will now be shaded using our deferred shader rlLoadDrawQuad(); rlDisableShader(); rlEnableColorBlend(); EndMode3D(); - // As a last step, we now copy over the depth buffer from our g-buffer to the default framebuffer - rlBindFramebuffer(RL_READ_FRAMEBUFFER, gBuffer.framebuffer); + // As a last step, we now copy over the depth buffer from our g-buffer to the default framebufferId + rlBindFramebuffer(RL_READ_FRAMEBUFFER, gBuffer.framebufferId); rlBindFramebuffer(RL_DRAW_FRAMEBUFFER, 0); rlBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, 0x00000100); // GL_DEPTH_BUFFER_BIT rlDisableFramebuffer(); @@ -290,7 +280,7 @@ int main(void) case DEFERRED_POSITION: { DrawTextureRec((Texture2D){ - .id = gBuffer.positionTexture, + .id = gBuffer.positionTextureId, .width = screenWidth, .height = screenHeight, }, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, Vector2Zero(), RAYWHITE); @@ -300,7 +290,7 @@ int main(void) case DEFERRED_NORMAL: { DrawTextureRec((Texture2D){ - .id = gBuffer.normalTexture, + .id = gBuffer.normalTextureId, .width = screenWidth, .height = screenHeight, }, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, Vector2Zero(), RAYWHITE); @@ -310,7 +300,7 @@ int main(void) case DEFERRED_ALBEDO: { DrawTextureRec((Texture2D){ - .id = gBuffer.albedoSpecTexture, + .id = gBuffer.albedoSpecTextureId, .width = screenWidth, .height = screenHeight, }, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, Vector2Zero(), RAYWHITE); @@ -331,18 +321,20 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - UnloadModel(model); // Unload the models + // Unload the models + UnloadModel(model); UnloadModel(cube); - UnloadShader(deferredShader); // Unload shaders + // Unload shaders + UnloadShader(deferredShader); UnloadShader(gbufferShader); // Unload geometry buffer and all attached textures - rlUnloadFramebuffer(gBuffer.framebuffer); - rlUnloadTexture(gBuffer.positionTexture); - rlUnloadTexture(gBuffer.normalTexture); - rlUnloadTexture(gBuffer.albedoSpecTexture); - rlUnloadTexture(gBuffer.depthRenderbuffer); + rlUnloadFramebuffer(gBuffer.framebufferId); + rlUnloadTexture(gBuffer.positionTextureId); + rlUnloadTexture(gBuffer.normalTextureId); + rlUnloadTexture(gBuffer.albedoSpecTextureId); + rlUnloadTexture(gBuffer.depthRenderbufferId); CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- From cfb81e4a0000b1ad1bd5726a697aaa66c82ba73d Mon Sep 17 00:00:00 2001 From: Iisakki Rotko Date: Mon, 12 Jan 2026 22:58:41 +0100 Subject: [PATCH 114/117] fix zig build accessing env vars (#5490) --- build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 111a1bcb1..c62e48642 100644 --- a/build.zig +++ b/build.zig @@ -448,7 +448,7 @@ pub const Options = struct { .linux_display_backend = b.option(LinuxDisplayBackend, "linux_display_backend", "Linux display backend to use") orelse defaults.linux_display_backend, .opengl_version = b.option(OpenglVersion, "opengl_version", "OpenGL version to use") orelse defaults.opengl_version, .config = b.option([]const u8, "config", "Compile with custom define macros overriding config.h") orelse &.{}, - .android_ndk = b.option([]const u8, "android_ndk", "specify path to android ndk") orelse std.process.getEnvVarOwned(b.allocator, "ANDROID_NDK_HOME") catch "", + .android_ndk = b.option([]const u8, "android_ndk", "specify path to android ndk") orelse "", .android_api_version = b.option([]const u8, "android_api_version", "specify target android API level") orelse defaults.android_api_version, }; } From 132151cf280b738c2a0e8b1fb0c5e081a1366478 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 13 Jan 2026 16:27:16 +0100 Subject: [PATCH 115/117] Revert "Update build.zig to work with 0.16 (#5487)" This reverts commit 32e7732061ec425aa261e7eede3b483fa33adbef. --- build.zig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/build.zig b/build.zig index 111a1bcb1..ab98bbe98 100644 --- a/build.zig +++ b/build.zig @@ -522,13 +522,12 @@ fn addExamples( raylib: *std.Build.Step.Compile, ) !*std.Build.Step { const all = b.step(module, "All " ++ module ++ " examples"); - const io = all.owner.graph.io; const module_subpath = b.pathJoin(&.{ "examples", module }); - var dir = try std.Io.Dir.cwd().openDir(io, b.pathFromRoot(module_subpath), .{ .iterate = true }); - defer dir.close(io); + var dir = try std.fs.cwd().openDir(b.pathFromRoot(module_subpath), .{ .iterate = true }); + defer dir.close(); var iter = dir.iterate(); - while (try iter.next(io)) |entry| { + while (try iter.next()) |entry| { if (entry.kind != .file) continue; const extension_idx = std.mem.lastIndexOf(u8, entry.name, ".c") orelse continue; const name = entry.name[0..extension_idx]; From 4be6815b3b15e6ec9b37c12bc111fab7ab340394 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 13 Jan 2026 16:27:51 +0100 Subject: [PATCH 116/117] Revert "fix zig build accessing env vars (#5490)" This reverts commit cfb81e4a0000b1ad1bd5726a697aaa66c82ba73d. --- build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 50222a109..ab98bbe98 100644 --- a/build.zig +++ b/build.zig @@ -448,7 +448,7 @@ pub const Options = struct { .linux_display_backend = b.option(LinuxDisplayBackend, "linux_display_backend", "Linux display backend to use") orelse defaults.linux_display_backend, .opengl_version = b.option(OpenglVersion, "opengl_version", "OpenGL version to use") orelse defaults.opengl_version, .config = b.option([]const u8, "config", "Compile with custom define macros overriding config.h") orelse &.{}, - .android_ndk = b.option([]const u8, "android_ndk", "specify path to android ndk") orelse "", + .android_ndk = b.option([]const u8, "android_ndk", "specify path to android ndk") orelse std.process.getEnvVarOwned(b.allocator, "ANDROID_NDK_HOME") catch "", .android_api_version = b.option([]const u8, "android_api_version", "specify target android API level") orelse defaults.android_api_version, }; } From 4b74312860e16de6272803e1ae7abfde006c31c9 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 13 Jan 2026 20:03:30 +0100 Subject: [PATCH 117/117] Update ROADMAP.md --- ROADMAP.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 9a8111133..a49cdbfd7 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -15,13 +15,13 @@ _Current version of raylib is complete and functional but there is always room f **raylib 5.x** - [ ] `rcore`: Support additional platforms: iOS, consoles? - - [ ] `rcore_web`: Avoid GLFW dependency, functionality can be directly implemented using emscripten SDK + - [x] `rcore_web`: Avoid GLFW dependency, functionality can be directly implemented using emscripten SDK - [ ] `rlgl`: Review GLSL shaders naming conventions for consistency - [ ] `textures`: Improve compressed textures support, loading and saving - [ ] `rmodels`: Improve 3d objects loading, specially animations (obj, gltf) - [ ] `raudio`: Implement miniaudio high-level provided features - - [ ] `examples`: Review all examples, add more and better code explanations - - [ ] Software renderer backend? Maybe using `Image` provided API + - [x] `examples`: Review all examples, add more and better code explanations + - [x] Software renderer backend? Maybe using `Image` provided API **raylib 4.x** - [x] Split core module into separate platforms?