diff --git a/BINDINGS.md b/BINDINGS.md
index ee7da46f4..6ef57035f 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 |
@@ -28,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 |
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/README.md b/README.md
index 875792f18..37e37c7c4 100644
--- a/README.md
+++ b/README.md
@@ -140,7 +140,7 @@ contributors
------------
-
+
license
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?
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._
diff --git a/build.zig b/build.zig
index 239b10f9e..ab98bbe98 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");
@@ -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", .{});
diff --git a/examples/Makefile b/examples/Makefile
index bc2afbb3c..acfcb0857 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,15 +205,17 @@ 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
+ EMMAKE := $(shell command -v emmake)
ifneq (, $(EMMAKE))
- MAKE = emmake make
+ MAKE = $(EMMAKE) make
else
MAKE = mingw32-make
endif
@@ -542,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 \
@@ -575,6 +578,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 \
@@ -605,6 +609,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 f36113f15..3841dff29 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,12 +208,12 @@ 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
+ EMMAKE := $(shell command -v emmake)
ifneq (, $(EMMAKE))
- MAKE = emmake make
+ MAKE = $(EMMAKE) make
else
MAKE = mingw32-make
endif
@@ -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 \
@@ -563,6 +564,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 \
@@ -593,6 +595,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 \
@@ -818,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)
@@ -914,6 +920,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)
@@ -1005,6 +1014,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
@@ -1367,15 +1379,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..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: 205]
+## 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.
@@ -69,11 +69,12 @@ Examples using raylib [core](../src/rcore.c) module platform functionality: wind
| [core_directory_files](core/core_directory_files.c) |
| ⭐☆☆☆ | 5.5 | 5.6 | [Hugo ARNAL](https://github.com/hugoarnal) |
| [core_highdpi_testbed](core/core_highdpi_testbed.c) |
| ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) |
| [core_screen_recording](core/core_screen_recording.c) |
| ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) |
-| [core_clipboard_text](core/core_clipboard_text.c) |
| ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Ananth S](https://github.com/Ananth1839) |
+| [core_clipboard_text](core/core_clipboard_text.c) |
| ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Ananth S](https://github.com/Ananth1839) |
| [core_text_file_loading](core/core_text_file_loading.c) |
| ⭐☆☆☆ | 5.5 | 5.6 | [Aanjishnu Bhattacharyya](https://github.com/NimComPoo-04) |
| [core_compute_hash](core/core_compute_hash.c) |
| ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) |
+| [core_keyboard_testbed](core/core_keyboard_testbed.c) |
| ⭐⭐☆☆ | 5.6 | 5.6 | [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,8 +118,9 @@ Examples using raylib shapes drawing functionality, provided by raylib [shapes](
| [shapes_rlgl_triangle](shapes/shapes_rlgl_triangle.c) |
| ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Robin](https://github.com/RobinsAviary) |
| [shapes_ball_physics](shapes/shapes_ball_physics.c) |
| ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [David Buzatto](https://github.com/davidbuzatto) |
| [shapes_penrose_tile](shapes/shapes_penrose_tile.c) |
| ⭐⭐⭐⭐️ | 5.5 | 5.6-dev | [David Buzatto](https://github.com/davidbuzatto) |
+| [shapes_hilbert_curve](shapes/shapes_hilbert_curve.c) |
| ⭐⭐⭐☆ | 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.
@@ -153,6 +155,7 @@ Examples using raylib textures functionality, including image/textures loading/g
| [textures_textured_curve](textures/textures_textured_curve.c) |
| ⭐⭐⭐☆ | 4.5 | 4.5 | [Jeffery Myers](https://github.com/JeffM2501) |
| [textures_sprite_stacking](textures/textures_sprite_stacking.c) |
| ⭐⭐☆☆ | 5.6-dev | 6.0 | [Robin](https://github.com/RobinsAviary) |
| [textures_cellular_automata](textures/textures_cellular_automata.c) |
| ⭐⭐☆☆ | 5.6 | 5.6 | [Jordi Santonja](https://github.com/JordSant) |
+| [textures_framebuffer_rendering](textures/textures_framebuffer_rendering.c) |
| ⭐⭐☆☆ | 5.6 | 5.6 | [Jack Boakes](https://github.com/jackboakes) |
### category: text [16]
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_highdpi_testbed.c b/examples/core/core_highdpi_testbed.c
index 6a036bbfc..a925527db 100644
--- a/examples/core/core_highdpi_testbed.c
+++ b/examples/core/core_highdpi_testbed.c
@@ -27,12 +27,13 @@ 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();
Vector2 mousePos = GetMousePosition();
int currentMonitor = GetCurrentMonitor();
+ Vector2 windowPos = GetWindowPosition();
int gridSpacing = 40; // Grid spacing in pixels
@@ -47,8 +48,10 @@ int main(void)
mousePos = GetMousePosition();
currentMonitor = GetCurrentMonitor();
scaleDpi = GetWindowScaleDPI();
+ windowPos = GetWindowPosition();
if (IsKeyPressed(KEY_SPACE)) ToggleBorderlessWindowed();
+ if (IsKeyPressed(KEY_F)) ToggleFullscreen();
//----------------------------------------------------------------------------------
// Draw
@@ -58,12 +61,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);
@@ -72,9 +75,14 @@ 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", (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);
+
+ // 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);
diff --git a/examples/core/core_highdpi_testbed.png b/examples/core/core_highdpi_testbed.png
index da99bbb0d..a37c82130 100644
Binary files a/examples/core/core_highdpi_testbed.png and b/examples/core/core_highdpi_testbed.png differ
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_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 000000000..bac0fc29a
Binary files /dev/null and b/examples/core/core_keyboard_testbed.png differ
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/examples_list.txt b/examples/examples_list.txt
index 3310cf2d2..eda62ee13 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
@@ -54,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
@@ -92,6 +95,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
@@ -121,6 +125,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
+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
diff --git a/examples/models/models_decals.c b/examples/models/models_decals.c
index f556139e1..71122dd68 100644
--- a/examples/models/models_decals.c
+++ b/examples/models/models_decals.c
@@ -45,7 +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);
-#define FreeDecalMeshData() GenMeshDecal((Model){ .meshCount = -1.0f }, (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);
//------------------------------------------------------------------------------------
@@ -198,12 +198,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 +215,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 +240,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 +596,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/models/models_first_person_maze.c b/examples/models/models_first_person_maze.c
index 43020974f..4c77d6121 100644
--- a/examples/models/models_first_person_maze.c
+++ b/examples/models/models_first_person_maze.c
@@ -80,12 +80,17 @@ 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++)
+ // Avoid map accessing out of bounds
+ if ((y < 0) || (y >= cubicmap.height)) continue;
+
+ for (int x = playerCellX - 1; x <= playerCellX + 1; x++)
{
+ // 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 })))
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
//--------------------------------------------------------------------------------------
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_hilbert_curve.c b/examples/shapes/shapes_hilbert_curve.c
new file mode 100644
index 000000000..3f368ca03
--- /dev/null
+++ b/examples/shapes/shapes_hilbert_curve.c
@@ -0,0 +1,196 @@
+/*******************************************************************************************
+*
+* raylib [shapes] example - hilbert curve
+*
+* 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) 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) 2025 Hamza RAHAL (@hmz-rhl)
+*
+********************************************************************************************/
+
+
+#include "raylib.h"
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"
+
+#include // Required for: calloc(), free()
+
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
+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
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [shapes] example - hilbert curve");
+
+ 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
+ //--------------------------------------------------------------------------------------
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ // 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))
+ {
+ UnloadHilbertPath(hilbertPath);
+ hilbertPath = LoadHilbertPath(order, size, &strokeCount);
+
+ if (animate) counter = 0;
+ else counter = strokeCount;
+
+ prevOrder = order;
+ prevSize = size;
+ }
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //--------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ if (counter < strokeCount)
+ {
+ // 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;
+ }
+ 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();
+ //--------------------------------------------------------------------------
+ }
+ //--------------------------------------------------------------------------------------
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ UnloadHilbertPath(hilbertPath);
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+ return 0;
+}
+
+//------------------------------------------------------------------------------------
+// Module Functions Definition
+//------------------------------------------------------------------------------------
+// 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;
+
+ Vector2 *hilbertPath = (Vector2 *)RL_CALLOC(*strokeCount, sizeof(Vector2));
+
+ for (int i = 0; i < *strokeCount; i++)
+ {
+ hilbertPath[i] = ComputeHilbertStep(order, i);
+ hilbertPath[i].x = hilbertPath[i].x*len + len/2.0f;
+ hilbertPath[i].y = hilbertPath[i].y*len + len/2.0f;
+ }
+
+ return hilbertPath;
+}
+
+// Unload Hilbert path data
+static void UnloadHilbertPath(Vector2 *hilbertPath)
+{
+ RL_FREE(hilbertPath);
+}
+
+// Compute Hilbert path U positions
+static Vector2 ComputeHilbertStep(int order, int index)
+{
+ // Hilbert points base pattern
+ static const Vector2 hilbertPoints[4] = {
+ [0] = { .x = 0, .y = 0 },
+ [1] = { .x = 0, .y = 1 },
+ [2] = { .x = 1, .y = 1 },
+ [3] = { .x = 1, .y = 0 },
+ };
+
+ int hilbertIndex = index&3;
+ Vector2 vect = hilbertPoints[hilbertIndex];
+ float temp = 0.0f;
+ int len = 0;
+
+ for (int j = 1; j < order; j++)
+ {
+ index = index >> 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
new file mode 100644
index 000000000..af99cbc49
Binary files /dev/null and b/examples/shapes/shapes_hilbert_curve.png differ
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/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,
diff --git a/examples/textures/textures_framebuffer_rendering.c b/examples/textures/textures_framebuffer_rendering.c
new file mode 100644
index 000000000..484192739
--- /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 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
diff --git a/examples/textures/textures_framebuffer_rendering.png b/examples/textures/textures_framebuffer_rendering.png
new file mode 100644
index 000000000..e6829f0bd
Binary files /dev/null and b/examples/textures/textures_framebuffer_rendering.png differ
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/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)
diff --git a/projects/VS2022/examples/core_keyboard_testbed.vcxproj b/projects/VS2022/examples/core_keyboard_testbed.vcxproj
new file mode 100644
index 000000000..2278f4ec5
--- /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
+
+
+
+ {D35D2FDA-B53F-4F70-81CA-24D95812B89C}
+ 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/examples/shapes_hilbert_curve.vcxproj b/projects/VS2022/examples/shapes_hilbert_curve.vcxproj
new file mode 100644
index 000000000..6c60841fb
--- /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
+ _CRT_SECURE_NO_WARNIGNS;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
+ _CRT_SECURE_NO_WARNIGNS;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
+ _CRT_SECURE_NO_WARNIGNS;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
+ _CRT_SECURE_NO_WARNIGNS;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
+ _CRT_SECURE_NO_WARNIGNS;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
+ _CRT_SECURE_NO_WARNIGNS;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
+ _CRT_SECURE_NO_WARNIGNS;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
+ _CRT_SECURE_NO_WARNIGNS;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/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
diff --git a/projects/VS2022/examples/textures_framebuffer_rendering.vcxproj b/projects/VS2022/examples/textures_framebuffer_rendering.vcxproj
new file mode 100644
index 000000000..3e845545e
--- /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
+
+
+
+ {F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}
+ 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/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln
index 50fef1cf5..47e83ef4c 100644
--- a/projects/VS2022/raylib.sln
+++ b/projects/VS2022/raylib.sln
@@ -431,6 +431,12 @@ 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
+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", "{F8DC77C0-556C-4672-B5B3-D2FA4ADC505C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug.DLL|ARM64 = Debug.DLL|ARM64
@@ -5365,6 +5371,78 @@ 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
+ {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
@@ -5532,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} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9}
+ {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}
@@ -5541,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}
@@ -5582,6 +5660,9 @@ 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}
+ {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}
diff --git a/projects/VS2022/raylib/raylib.vcxproj b/projects/VS2022/raylib/raylib.vcxproj
index 3a7082d77..8cc3fae7a 100644
--- a/projects/VS2022/raylib/raylib.vcxproj
+++ b/projects/VS2022/raylib/raylib.vcxproj
@@ -582,7 +582,6 @@
-
@@ -602,7 +601,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/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/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
diff --git a/src/Makefile b/src/Makefile
index bc84abece..459b79f83 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.
@@ -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 b749f8952..5e7c6f1d2 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.
@@ -30,17 +30,24 @@
//------------------------------------------------------------------------------------
// 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
-#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
// 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
@@ -69,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
+#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
@@ -94,8 +101,15 @@
#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
#define MAX_FILEPATH_CAPACITY 8192 // Maximum file paths capacity
#define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value)
@@ -105,7 +119,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
@@ -116,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
@@ -124,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)
@@ -173,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
//------------------------------------------------------------------------------------
@@ -184,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
@@ -207,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
@@ -230,6 +250,7 @@
// Support conservative font atlas size estimation
//#define SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE 1
+#endif
// rtext: Configuration values
//------------------------------------------------------------------------------------
@@ -240,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
@@ -250,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
//------------------------------------------------------------------------------------
@@ -264,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
@@ -272,6 +296,7 @@
//#define SUPPORT_FILEFORMAT_FLAC 1
#define SUPPORT_FILEFORMAT_XM 1
#define SUPPORT_FILEFORMAT_MOD 1
+#endif
// raudio: Configuration values
//------------------------------------------------------------------------------------
@@ -281,18 +306,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
-// NOTE: By default LOG_DEBUG traces not shown
-#define SUPPORT_TRACELOG 1
-//#define SUPPORT_TRACELOG_DEBUG 1
-
-// utils: Configuration values
-//------------------------------------------------------------------------------------
-#define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message
-
#endif // CONFIG_H
diff --git a/src/external/RGFW.h b/src/external/RGFW.h
index 7205bf9d8..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 */
@@ -6522,8 +6523,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);
@@ -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);
}
@@ -6968,7 +6969,8 @@ 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.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.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 + (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 */
@@ -7064,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;
@@ -7898,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);
}
diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h
index 29500f3cf..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.
@@ -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;
@@ -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;
@@ -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];
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();
}
diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c
index 7b8d3e052..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-
@@ -27,7 +24,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.
@@ -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
@@ -267,6 +268,19 @@ 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())
+
+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
//----------------------------------------------------------------------------------
@@ -358,9 +372,9 @@ 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);
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN);
}
// Clear window configuration state flags
@@ -817,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
//----------------------------------------------------------------------------
@@ -885,7 +897,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;
@@ -919,6 +930,7 @@ static int InitGraphicsDevice(void)
// Get an EGL device connection
platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
if (platform.device == EGL_NO_DISPLAY)
{
TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
@@ -1238,8 +1250,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;
@@ -1332,30 +1347,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
@@ -1371,35 +1373,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;
@@ -1417,4 +1446,144 @@ 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;
+ }
+}
+
+// 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 efa146fd0..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-
@@ -30,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.
@@ -176,45 +173,72 @@ 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)
+ // 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.fullscreen = false;
- FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+ CORE.Window.position = (Point){ 0, 0 };
+ CORE.Window.screen = CORE.Window.display;
- 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);
+#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);
}
+ 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 = (unsigned int)(CORE.Window.screen.width * scaleDpi.x);
+ CORE.Window.screen.height = (unsigned int)(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;
+ // 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);
+
+#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)
@@ -226,13 +250,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);
@@ -248,7 +267,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
@@ -256,22 +275,13 @@ 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);
- const int monitorWidth = mode->width;
- const int monitorHeight = mode->height;
+ glfwGetMonitorPos(monitors[monitor], &CORE.Window.position.x, &CORE.Window.position.y);
+ 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);
@@ -280,39 +290,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 = (unsigned int)(CORE.Window.screen.width * scaleDpi.x);
+ CORE.Window.screen.height = (unsigned int)(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");
@@ -666,7 +669,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]));
@@ -902,7 +905,7 @@ Vector2 GetMonitorPosition(int monitor)
if ((monitor >= 0) && (monitor < monitorCount))
{
- int x = 0;
+ int x = 0;
int y = 0;
glfwGetMonitorPos(monitors[monitor], &x, &y);
@@ -1013,19 +1016,15 @@ 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
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;
}
@@ -1266,7 +1265,13 @@ 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 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 0.0f default when gamepad is not connected
+ state.axes[GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
+ state.axes[GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
+ }
const unsigned char *buttons = state.buttons;
@@ -1337,7 +1342,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)
@@ -1356,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)
{
@@ -1416,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
@@ -1449,7 +1451,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);
@@ -1457,7 +1459,7 @@ int InitPlatform(void)
glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE);
#endif
}
- else
+ else
{
glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
#if defined(__APPLE__)
@@ -1530,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");
@@ -1548,80 +1553,40 @@ 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
- 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))
+ // Check if user requested some screen size
+ if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0))
{
- // 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;
+ // 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;
}
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;
+ CORE.Window.previousScreen = CORE.Window.screen;
+ CORE.Window.screen = CORE.Window.display;
}
- 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);
+ platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", monitor, NULL);
if (!platform.handle)
{
glfwTerminate();
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
{
- // 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();
@@ -1630,7 +1595,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();
@@ -1638,7 +1603,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
@@ -1649,7 +1614,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
{
@@ -1666,13 +1631,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
@@ -1691,6 +1656,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
@@ -1711,37 +1678,34 @@ 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];
+
int monitorX = 0;
int monitorY = 0;
int monitorWidth = 0;
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;
- 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
@@ -1868,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
@@ -1890,7 +1873,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;
@@ -1901,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
@@ -1962,7 +1944,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_rgfw.c b/src/platforms/rcore_desktop_rgfw.c
index 2671538d8..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
@@ -29,7 +26,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.
@@ -290,14 +287,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 +301,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 +326,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 +345,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
@@ -385,7 +380,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))
{
@@ -459,7 +454,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))
{
@@ -510,7 +505,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))
{
@@ -522,46 +517,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 +542,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);
}
}
@@ -1284,13 +1253,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);
}
@@ -1341,10 +1308,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_sdl.c b/src/platforms/rcore_desktop_sdl.c
index add1de6ad..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-
@@ -29,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.
@@ -472,13 +469,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 +549,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 +639,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))
{
@@ -1428,12 +1422,12 @@ 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
- 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 +1438,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
@@ -1490,7 +1484,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))
{
@@ -1937,11 +1931,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_desktop_win32.c b/src/platforms/rcore_desktop_win32.c
index 29702921f..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-
@@ -26,7 +23,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.
@@ -263,7 +260,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;
@@ -1877,14 +1874,24 @@ 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:
{
@@ -2039,8 +2046,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..a0ae1fa37 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
@@ -29,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.
@@ -89,6 +86,10 @@
#define EGL_OPENGL_ES3_BIT 0x40
#endif
+#ifndef EGL_PLATFORM_GBM_KHR
+ #define EGL_PLATFORM_GBM_KHR 0x31D7
+#endif
+
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
@@ -135,8 +136,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
@@ -265,6 +270,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
//----------------------------------------------------------------------------------
@@ -1113,9 +1120,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;
@@ -1147,7 +1151,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)
@@ -1412,9 +1415,27 @@ int InitPlatform(void)
};
EGLint numConfigs = 0;
+ const char *eglClientExtensions = NULL;
// Get an EGL device connection
- platform.device = eglGetDisplay((EGLNativeDisplayType)platform.gbmDevice);
+ // 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)
+ eglClientExtensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ if ((eglClientExtensions != NULL) && (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((EGLNativeDisplayType)platform.gbmDevice);
+#endif
if (platform.device == EGL_NO_DISPLAY)
{
TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
@@ -1494,8 +1515,21 @@ 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;
@@ -1564,7 +1598,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
@@ -1882,8 +1920,15 @@ 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++)
{
@@ -2046,17 +2091,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";
- platform.absRange.y = absinfo[ABS_Y].info.minimum;
- platform.absRange.height = absinfo[ABS_Y].info.maximum - absinfo[ABS_Y].info.minimum;
+ 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;
+
+ 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))
@@ -2127,18 +2204,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
@@ -2187,16 +2261,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;
}
}
}
@@ -2230,6 +2303,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))
@@ -2275,39 +2349,102 @@ 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_SLOT)
+ {
+ platform.touchSlot = event.value;
+ isMultitouch = true;
+ }
if (event.code == ABS_MT_POSITION_X)
{
- 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
+ isMultitouch = true;
+ 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
+ }
}
if (event.code == ABS_MT_POSITION_Y)
{
- 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 (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 ((event.value < 0) && (platform.touchSlot < MAX_TOUCH_POINTS))
+ 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;
+ 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
+ }
}
}
@@ -2319,16 +2456,15 @@ static void PollMouseEvents(void)
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)
{
platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1;
- touchAction = 1; // TOUCH_ACTION_DOWN
+ touchAction = 1; // TOUCH_ACTION_DOWN
}
}
-
}
// Button parsing
@@ -2339,8 +2475,43 @@ 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;
@@ -2355,24 +2526,40 @@ 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;
}
- // 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;
}
#if defined(SUPPORT_GESTURES_SYSTEM)
if (touchAction > -1)
{
GestureEvent gestureEvent = { 0 };
-
gestureEvent.touchAction = touchAction;
gestureEvent.pointCount = CORE.Input.Touch.pointCount;
@@ -2383,7 +2570,6 @@ static void PollMouseEvents(void)
}
ProcessGestureEvent(gestureEvent);
-
touchAction = -1;
}
#endif
@@ -2479,4 +2665,82 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt
return nearestIndex;
}
-// EOF
+// 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
\ No newline at end of file
diff --git a/src/platforms/rcore_memory.c b/src/platforms/rcore_memory.c
index 1b7a55fd8..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-
@@ -27,7 +24,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.
@@ -472,7 +469,7 @@ void PollInputEvents(void)
}
// TODO: Poll input events for current platform
-
+
// Check for key pressed to exit
if (kbhit())
{
@@ -513,7 +510,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_template.c b/src/platforms/rcore_template.c
index 1f8c5242b..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.
@@ -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 934f778c3..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-
@@ -26,7 +23,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.
@@ -76,10 +73,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
@@ -204,7 +201,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 +209,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 +232,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 +268,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 +279,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 +305,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 +536,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);
}
@@ -892,7 +882,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;
@@ -1111,7 +1101,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
@@ -1135,7 +1125,7 @@ void PollInputEvents(void)
int InitPlatform(void)
{
SetCanvasIdJs(platform.canvasId, 64); // Get the current canvas id
-
+
glfwSetErrorCallback(ErrorCallback);
// Initialize GLFW internal global state
@@ -1155,8 +1145,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
@@ -1209,8 +1197,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
}
@@ -1245,7 +1233,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);
@@ -1253,14 +1241,14 @@ 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
// 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))
@@ -1299,18 +1287,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...
@@ -1531,7 +1507,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);
}
}
}
@@ -1716,12 +1692,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))
@@ -1830,7 +1806,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
new file mode 100644
index 000000000..36b8e964a
--- /dev/null
+++ b/src/platforms/rcore_web_emscripten.c
@@ -0,0 +1,1690 @@
+/**********************************************************************************************
+*
+* rcore_web_emscripten - Functions to manage window, graphics device and inputs
+*
+* PLATFORM: WEB - EMSCRIPTEN
+* - HTML5 (WebAssembly)
+*
+* LIMITATIONS:
+* - TBD
+*
+* POSSIBLE IMPROVEMENTS:
+* - TBD
+*
+* 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-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.
+*
+* 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(););
+
+ 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);
+ );
+
+ 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 (!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:
+ // 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);
+
+ 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);
+
+ 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(););
+
+ 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(););
+ }
+
+ 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;
+ }
+
+ //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
+ 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
+ // 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)
+ {
+ 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));
+ strncpy(CORE.Window.dropFilepaths[i], paths[i], MAX_FILEPATH_LENGTH - 1);
+ }
+ }
+}
+*/
+
+// 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)
+{
+ /*
+ 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++) 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))
+ {
+ 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
diff --git a/src/raudio.c b/src/raudio.c
index c65aaa134..18f9e0aad 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.
@@ -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)
@@ -1134,7 +1130,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");
@@ -1249,7 +1245,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;
}
@@ -2743,9 +2739,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/raylib.h b/src/raylib.h
index ba80e40c7..177138dc9 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.
@@ -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
@@ -1075,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
@@ -1123,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/raymath.h b/src/raymath.h
index 32dfd2b0a..57e3dac51 100644
--- a/src/raymath.h
+++ b/src/raymath.h
@@ -19,20 +19,25 @@
*
* 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)
+* 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.
@@ -79,7 +84,6 @@
#endif
#endif
-
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
@@ -170,6 +174,35 @@ typedef struct float16 {
#include // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabsf()
+#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:
+ // - SSEx, AVX, AVX2, FMA, NEON, RVV
+ /*
+ #if defined(__SSE4_2__)
+ #include
+ #define RAYMATH_SSE42_ENABLED
+ #elif defined(__SSE4_1__)
+ #include
+ #define RAYMATH_SSE41_ENABLED
+ #elif defined(__SSSE3__)
+ #include
+ #define RAYMATH_SSSE3_ENABLED
+ #elif defined(__SSE3__)
+ #include
+ #define RAYMATH_SSE3_ENABLED
+ #elif defined(__SSE2__) || (defined(_M_AMD64) || defined(_M_X64)) // SSE2 x64
+ #include
+ #define RAYMATH_SSE2_ENABLED
+ #endif
+ */
+ #if defined(__SSE__) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP >= 1))
+ #include
+ #define RAYMATH_SSE_ENABLED
+ #endif
+#endif
+
//----------------------------------------------------------------------------------
// Module Functions Definition - Utils math
//----------------------------------------------------------------------------------
@@ -1647,7 +1680,64 @@ RMAPI Matrix MatrixSubtract(Matrix left, Matrix right)
RMAPI Matrix MatrixMultiply(Matrix left, Matrix right)
{
Matrix result = { 0 };
+
+#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
+ _MM_TRANSPOSE4_PS(c0, c1, c2, c3);
+ float tmp[4] = { 0 };
+ __m128 row;
+
+ // 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,6 +1754,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;
}
diff --git a/src/rcamera.h b/src/rcamera.h
index 3e9f83095..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.
@@ -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
@@ -252,8 +254,11 @@ 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.7071f) forward.z = 0;
+ else if (fabsf(camera->up.x) > 0.7071f) forward.x = 0;
+ else forward.y = 0;
+
forward = Vector3Normalize(forward);
}
@@ -285,8 +290,11 @@ 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.7071f) right.z = 0;
+ else if (fabsf(camera->up.x) > 0.7071f) right.x = 0;
+ else right.y = 0;
+
right = Vector3Normalize(right);
}
@@ -345,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
@@ -382,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);
diff --git a/src/rcore.c b/src/rcore.c
index 1f47efcb9..dd4123a3b 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.
@@ -104,16 +104,12 @@
#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 "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()]
@@ -205,16 +201,22 @@
#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
//----------------------------------------------------------------------------------
// 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
@@ -273,7 +275,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
@@ -287,20 +289,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)
@@ -316,17 +317,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,
+ // 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;
@@ -342,18 +343,19 @@ 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
} 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 {
@@ -385,10 +387,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)
@@ -493,7 +503,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
@@ -762,31 +771,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 +807,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
@@ -817,7 +826,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;
@@ -1100,7 +1109,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));
@@ -1735,7 +1744,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
@@ -1856,9 +1865,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
@@ -1974,11 +2363,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
@@ -2261,7 +2646,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);
@@ -2269,7 +2654,7 @@ const char *GetApplicationDirectory(void)
#else
len = GetModuleFileNameA(NULL, appDir, MAX_PATH);
#endif
-
+
if (len > 0)
{
for (int i = len; i >= 0; --i)
@@ -2286,7 +2671,7 @@ const char *GetApplicationDirectory(void)
appDir[0] = '.';
appDir[1] = '\\';
}
-
+
#elif defined(__linux__)
unsigned int size = sizeof(appDir);
@@ -2308,7 +2693,7 @@ const char *GetApplicationDirectory(void)
appDir[0] = '.';
appDir[1] = '/';
}
-
+
#elif defined(__APPLE__)
uint32_t size = sizeof(appDir);
@@ -2330,7 +2715,7 @@ const char *GetApplicationDirectory(void)
appDir[0] = '.';
appDir[1] = '/';
}
-
+
#elif defined(__FreeBSD__)
size_t size = sizeof(appDir);
@@ -2701,7 +3086,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;
@@ -3233,14 +3618,23 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName)
#if defined(SUPPORT_AUTOMATION_EVENTS)
// Export events as binary file
- // TODO: Save to memory buffer and SaveFileData()
+ // NOTE: Code not used, only for reference if required in the future
/*
- 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);
+ if (list.count > 0)
+ {
+ 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);
+ }
*/
// Export events as text
@@ -3257,7 +3651,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
@@ -3828,84 +4222,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
@@ -4186,22 +4502,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
}
@@ -4323,7 +4637,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/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 cda64896c..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
*
@@ -89,7 +85,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.
@@ -132,7 +128,6 @@
// Support TRACELOG macros
#ifndef TRACELOG
#define TRACELOG(level, ...) (void)0
- #define TRACELOGD(...) (void)0
#endif
// Allow custom memory allocators
@@ -197,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
//----------------------------------------------------------------------------------
@@ -1864,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
}
@@ -1873,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;
@@ -1882,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
}
@@ -1890,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
}
@@ -1898,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
}
@@ -1907,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;
@@ -3324,7 +3315,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)
{
@@ -3391,7 +3382,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);
@@ -3827,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
@@ -3839,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)
@@ -3879,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);
@@ -3910,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
@@ -4246,7 +4237,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/rmodels.c b/src/rmodels.c
index 40af4afc4..58b08350d 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.
@@ -42,14 +42,10 @@
#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)
-#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
@@ -132,7 +128,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
@@ -1762,11 +1758,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();
@@ -1987,7 +1986,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);
@@ -4153,7 +4152,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;
@@ -6353,7 +6352,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 +6472,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++)
{
@@ -7042,8 +7041,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);
diff --git a/src/rshapes.c b/src/rshapes.c
index 528a362d5..3f686f21a 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.
@@ -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 7c25fde0b..8085e81c8 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.
@@ -55,14 +55,10 @@
#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)
-#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()
@@ -700,9 +696,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:
@@ -742,15 +738,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);
-
+
Image imSpace = {
- .data = RL_CALLOC(glyphs[k].advanceX*fontSize, 2),
+ .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;
}
@@ -853,7 +853,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(atlasDataSize, 1); // Create a bitmap to store characters (8 bpp)
atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
atlas.mipmaps = 1;
@@ -898,7 +899,15 @@ 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: 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];
+ }
}
}
@@ -946,7 +955,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 +977,17 @@ 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: ensure the atlas is large enough to hold a 3x3 rectangle
+ 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
@@ -1011,7 +1031,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");
}
}
@@ -1042,7 +1062,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");
@@ -1518,7 +1538,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;
@@ -1597,14 +1617,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 +1736,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 +1753,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 +1766,23 @@ char *TextReplace(const char *text, const char *search, const char *replacement)
{
insertPoint = (char *)strstr(text, search);
lastReplacePos = (int)(insertPoint - text);
+
+ // 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)
- strcpy(temp, text);
+ strcpy(temp, text); // OK
+ //strncpy(temp, text, tempLen - 1); // WRONG
}
return result;
@@ -2047,7 +2079,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
@@ -2084,13 +2116,13 @@ int *LoadCodepoints(const char *text, int *count)
{
int *codepoints = NULL;
int codepointCount = 0;
-
+
if (text != NULL)
{
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++)
@@ -2197,7 +2229,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;
@@ -2492,7 +2524,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..a20b5e516 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.
@@ -63,14 +63,10 @@
#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)
-#include "utils.h" // Required for: TRACELOG()
#include "rlgl.h" // OpenGL abstraction layer to multiple versions
#include // Required for: malloc(), calloc(), free()
@@ -765,7 +761,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");
@@ -1125,7 +1121,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 +1483,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')
@@ -2395,7 +2391,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 +2428,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/shell.html b/src/shell.html
index e6c80a39b..e5e26a3b1 100644
--- a/src/shell.html
+++ b/src/shell.html
@@ -179,7 +179,7 @@ jwE50AGjLCVuS8Yt4H7OgZLKK5EKOsLviEWJSL/+0uMi7gLUSBseYwqEbXvSHCec1CJvZPyHCmYQffaB
-
+
diff --git a/src/utils.c b/src/utils.c
deleted file mode 100644
index 09158893a..000000000
--- a/src/utils.c
+++ /dev/null
@@ -1,509 +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-2025 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-2025 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)
-{
- 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
- return 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
- return 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
- return fopen(TextFormat("%s/%s", internalDataPath, fileName), mode);
- #define fopen(name, mode) android_fopen(name, mode)
- }
- }
-}
-#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 271d0d2c7..000000000
--- a/src/utils.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/**********************************************************************************************
-*
-* raylib.utils - Some common utility functions
-*
-*
-* LICENSE: zlib/libpng
-*
-* Copyright (c) 2014-2025 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__)
-
- #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
-
-//----------------------------------------------------------------------------------
-// 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
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/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
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..d8f4a9521 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 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
@@ -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 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
@@ -105,6 +106,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 +136,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 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
@@ -209,7 +212,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 +222,9 @@ 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 | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c
index aa491ff23..ad9bcbed0 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.
@@ -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
@@ -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
@@ -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);
//------------------------------------------------------------------------------------------------
@@ -1003,6 +1004,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 +1014,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 +1087,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 +1095,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");
@@ -1226,6 +1244,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 +1355,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;
- }
//------------------------------------------------------------------------------------------------
}
@@ -1591,11 +1610,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
@@ -1858,7 +1882,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");
@@ -2417,7 +2441,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
@@ -2429,7 +2453,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]);
@@ -2587,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();
@@ -2671,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);
//----------------------------------------------------------------------------------------
@@ -2817,7 +2852,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) ", ")",
@@ -2871,8 +2906,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];
@@ -2912,13 +2947,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;
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/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 @@
+
+
+
+
+
+
+
+
+
+
+
+
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");