diff --git a/.github/workflows/parse_api.yml b/.github/workflows/parse_api.yml
index e095e4a55..e25cde18b 100644
--- a/.github/workflows/parse_api.yml
+++ b/.github/workflows/parse_api.yml
@@ -22,7 +22,7 @@ jobs:
- name: Diff parse files
id: diff
run: |
- git add -N tools/rlparser
+ git add -N tools/rlparser/output
git diff --name-only --exit-code
continue-on-error: true
@@ -32,6 +32,6 @@ jobs:
set -x
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- git add tools/rlparser
+ git add tools/rlparser/output
git commit -m "rlparser: update raylib_api.* by CI"
git push
diff --git a/.github/workflows/update_examples.yml b/.github/workflows/update_examples.yml
index 7feba7d89..c4ada2bd1 100644
--- a/.github/workflows/update_examples.yml
+++ b/.github/workflows/update_examples.yml
@@ -29,8 +29,9 @@ jobs:
shell: bash
- name: Build and run rexm tool (requires GNU Makefile)
- # "rexm update" validates and updates all required examples in raylib and even raylib.com repos,
- # note that it calls examples/Makefile.Web internally, so it requires [make] tool available
+ # "rexm validate" validates examples collection, looking for inconsistencies, it does not rebuild examples
+ # "rexm update" validates and updates all examples with inconsistencies, pushing fixes to raylib and raylib.com repos
+ # note that rexm calls examples/Makefile.Web internally, so it requires [make] tool available
run: |
sudo apt-get update && sudo apt-get install -y libopengl0 libglu1-mesa libx11-dev libxrandr-dev libxcursor-dev libxi-dev libxinerama-dev libgl1-mesa-dev libglu1-mesa-dev
cd "${{ github.workspace }}/src"
@@ -47,7 +48,7 @@ jobs:
export REXM_EXAMPLES_COLLECTION_FILE_PATH="${{ github.workspace }}/examples/examples_list.txt"
export REXM_EXAMPLES_VS2022_SLN_FILE="${{ github.workspace }}/projects/VS2022/raylib.sln"
export EMSDK_PATH="${{ github.workspace }}/emsdk-cache/emsdk-main"
- ./rexm update
+ ./rexm validate
shell: bash
- name: Commit changes to raylib repo (DISABLED)
diff --git a/.gitignore b/.gitignore
index fa4c4c49d..f7b2cccf6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,14 +58,23 @@ packages/
*.h.pch
./*.obj
+# Ignore SDL libs for testing
+src/external/SDL2
+src/external/SDL3
+
# Emscripten
emsdk
# Ignore wasm data in examples/
+examples/**/*
+!examples/**/*.*
+!examples/**/*/
+examples/**/*.exe
examples/**/*.wasm
examples/**/*.data
examples/**/*.js
examples/**/*.html
+examples/**/logs/*
# Ignore files build by xcode
*.mode*v*
@@ -115,7 +124,8 @@ build-*/
docgen_tmp/
# Tools stuff
-tools/parser/raylib_parser
+tools/parser/rlparser.exe
+tools/parser/rlparser
tools/rexm/rexm.exe
tools/rexm/rexm
diff --git a/BINDINGS.md b/BINDINGS.md
index 7770f41c2..ee7da46f4 100644
--- a/BINDINGS.md
+++ b/BINDINGS.md
@@ -29,8 +29,10 @@ Some people ported raylib to other languages in the form of bindings or wrappers
| [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 |
| [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 |
| [raylib-freebasic](https://github.com/WIITD/raylib-freebasic) | **5.0** | [FreeBASIC](https://www.freebasic.net) | MIT |
+| [raylib.f](https://github.com/cthulhuology/raylib.f) | **5.5** | [Forth](https://forth.com) | Zlib |
| [fortran-raylib](https://github.com/interkosmos/fortran-raylib) | **5.5** | [Fortran](https://fortran-lang.org) | ISC |
| [raylib-go](https://github.com/gen2brain/raylib-go) | **5.5** | [Go](https://golang.org) | Zlib |
| [raylib-guile](https://github.com/petelliott/raylib-guile) | **auto** | [Guile](https://www.gnu.org/software/guile) | Zlib |
@@ -74,6 +76,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers
| [raylib-rs](https://github.com/raylib-rs/raylib-rs) | **5.5** | [Rust](https://www.rust-lang.org) | Zlib |
| [raylib-ruby](https://github.com/wilsonsilva/raylib-ruby) | 4.5 | [Ruby](https://www.ruby-lang.org) | Zlib |
| [Relib](https://github.com/RedCubeDev-ByteSpace/Relib) | 3.5 | [ReCT](https://github.com/RedCubeDev-ByteSpace/ReCT) | **???** |
+| [ringraylib5](https://github.com/ring-lang/ring/tree/master/extensions/ringraylib5) | **5.0** | [Ring](https://ring-lang.github.io/) | **???** |
| [racket-raylib](https://github.com/eutro/racket-raylib) | **5.5** | [Racket](https://racket-lang.org) | MIT/Apache-2.0 |
| [raylib-swift](https://github.com/STREGAsGate/Raylib) | 4.0 | [Swift](https://swift.org) | MIT |
| [raylib-scopes](https://github.com/salotz/raylib-scopes) | auto | [Scopes](http://scopes.rocks) | MIT |
diff --git a/CHANGELOG b/CHANGELOG
index f792bca4d..29e4d8174 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -565,7 +565,7 @@ Detailed changes:
[rtext] ADDED: SetTextLineSpacing() to define line breaks text drawing spacing by @raysan5
[rtext] RENAMED: LoadFont*() parameter names for consistency and coherence by @raysan5
[rtext] REVIEWED: GetCodepointCount(), ignore unused return value of GetCodepointNext by @ashn-dot-dev
-[rtext] REVIEWED: TextFormat() warn user if buffer overflow occured (#3399) by @Murlocohol
+[rtext] REVIEWED: TextFormat() warn user if buffer overflow occurred (#3399) by @Murlocohol
[rtext] REVIEWED: TextFormat(), added "..." for truncation (#3366) by @raysan5
[rtext] REVIEWED: GetGlyphIndex() (#3000) by @raysan5
[rtext] REVIEWED: GetCodepointNext() to return default value by @chocolate42
diff --git a/CMakeOptions.txt b/CMakeOptions.txt
index 32cfb814e..cb1b95b0c 100644
--- a/CMakeOptions.txt
+++ b/CMakeOptions.txt
@@ -6,7 +6,7 @@ if(EMSCRIPTEN)
# When configuring web builds with "emcmake cmake -B build -S .", set PLATFORM to Web by default
SET(PLATFORM Web CACHE STRING "Platform to build for.")
endif()
-enum_option(PLATFORM "Desktop;Web;Android;Raspberry Pi;DRM;SDL" "Platform to build for.")
+enum_option(PLATFORM "Desktop;Web;Android;Raspberry Pi;DRM;SDL;RGFW" "Platform to build for.")
enum_option(OPENGL_VERSION "OFF;4.3;3.3;2.1;1.1;ES 2.0;ES 3.0;Software" "Force a specific OpenGL Version?")
diff --git a/CONVENTIONS.md b/CONVENTIONS.md
index 58cfa6270..86044193c 100644
--- a/CONVENTIONS.md
+++ b/CONVENTIONS.md
@@ -41,6 +41,7 @@ while (!WindowShouldClose())
}
+// Always use accumulators as `x++` instead of `++x`
for (int i = 0; i < NUM_VALUES; i++) printf("%i", i);
// Be careful with the switch formatting!
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/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 031a6824d..239b10f9e 100644
--- a/build.zig
+++ b/build.zig
@@ -106,9 +106,9 @@ const config_h_flags = outer: {
if (std.mem.startsWith(u8, line, "//")) continue;
if (std.mem.startsWith(u8, line, "#if")) continue;
- var flag = std.mem.trimLeft(u8, line, " \t"); // Trim whitespace
+ var flag = std.mem.trimStart(u8, line, " \t"); // Trim whitespace
flag = flag["#define ".len - 1 ..]; // Remove #define
- flag = std.mem.trimLeft(u8, flag, " \t"); // Trim whitespace
+ flag = std.mem.trimStart(u8, flag, " \t"); // Trim whitespace
flag = flag[0 .. std.mem.indexOf(u8, flag, " ") orelse continue]; // Flag is only one word, so capture till space
flag = "-D" ++ flag; // Prepend with -D
@@ -155,9 +155,9 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
);
}
- // Sets a flag indiciating the use of a custom `config.h`
- try raylib_flags_arr.append(b.allocator, "-DEXTERNAL_CONFIG_FLAGS");
if (options.config.len > 0) {
+ // Sets a flag indicating the use of a custom `config.h`
+ try raylib_flags_arr.append(b.allocator, "-DEXTERNAL_CONFIG_FLAGS");
// Splits a space-separated list of config flags into multiple flags
//
// Note: This means certain flags like `-x c++` won't be processed properly.
@@ -187,13 +187,13 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
try raylib_flags_arr.append(b.allocator, flag);
}
} else {
- // Set default config if no custome config got set
+ // Set default config if no custom config got set
try raylib_flags_arr.appendSlice(b.allocator, &config_h_flags);
}
// No GLFW required on PLATFORM_DRM
if (options.platform != .drm) {
- raylib.addIncludePath(b.path("src/external/glfw/include"));
+ raylib.root_module.addIncludePath(b.path("src/external/glfw/include"));
}
var c_source_files: std.ArrayList([]const u8) = try .initCapacity(b.allocator, 2);
@@ -224,7 +224,7 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
raylib.root_module.addCMacro(options.opengl_version.toCMacroStr(), "");
}
- raylib.addIncludePath(b.path("src/platforms"));
+ raylib.root_module.addIncludePath(b.path("src/platforms"));
switch (target.result.os.tag) {
.windows => {
switch (options.platform) {
@@ -438,7 +438,7 @@ pub const Options = struct {
pub fn getOptions(b: *std.Build) Options {
return .{
- .platform = b.option(PlatformBackend, "platform", "Choose the platform backedn for desktop target") orelse defaults.platform,
+ .platform = b.option(PlatformBackend, "platform", "Choose the platform backend for desktop target") orelse defaults.platform,
.raudio = b.option(bool, "raudio", "Compile with audio support") orelse defaults.raudio,
.rmodels = b.option(bool, "rmodels", "Compile with models support") orelse defaults.rmodels,
.rtext = b.option(bool, "rtext", "Compile with text support") orelse defaults.rtext,
diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake
index 8f127fc23..96abeea93 100644
--- a/cmake/LibraryConfigurations.cmake
+++ b/cmake/LibraryConfigurations.cmake
@@ -141,6 +141,8 @@ elseif ("${PLATFORM}" MATCHES "SDL")
add_compile_definitions(USING_SDL2_PACKAGE)
endif()
endif()
+elseif ("${PLATFORM}" MATCHES "RGFW")
+ set(PLATFORM_CPP "PLATFORM_DESKTOP_RGFW")
endif ()
if (NOT ${OPENGL_VERSION} MATCHES "OFF")
diff --git a/examples/Makefile b/examples/Makefile
index c8500b665..bc2afbb3c 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -77,7 +77,7 @@ RAYLIB_SRC_PATH ?= ../src
# Locations of raylib.h and libraylib.a/libraylib.so
# NOTE: Those variables are only used for PLATFORM_OS: LINUX, BSD
-DESTDIR ?= /usr/local
+DESTDIR ?= /usr/local
RAYLIB_INCLUDE_PATH ?= $(DESTDIR)/include
RAYLIB_LIB_PATH ?= $(DESTDIR)/lib
@@ -231,7 +231,7 @@ endif
# -Wno-missing-braces ignore invalid warning (GCC bug 53119)
# -Wno-unused-value ignore unused return values of some functions (i.e. fread())
# -D_DEFAULT_SOURCE use with -std=c99 on Linux and PLATFORM_WEB, required for timespec
-CFLAGS = -Wall -std=c99 -D_DEFAULT_SOURCE -Wno-missing-braces -Wunused-result
+CFLAGS = -Wall -std=c99 -D_DEFAULT_SOURCE -Wno-missing-braces -Wno-unused-result
ifeq ($(BUILD_MODE),DEBUG)
CFLAGS += -g -D_DEBUG
@@ -486,7 +486,19 @@ endif
ifeq ($(TARGET_PLATFORM),PLATFORM_DRM)
# Libraries for DRM compiling
# NOTE: Required packages: libasound2-dev (ALSA)
- LDLIBS = -lraylib -lGLESv2 -lEGL -lpthread -lrt -lm -lgbm -ldrm -ldl -latomic
+ LDLIBS = -lraylib -lGLESv2 -lEGL -ldrm -lgbm -lpthread -lrt -lm -ldl -latomic
+ # TODO: Examples compilation does not define GRAPHICS, is it required?
+ #ifeq ($(GRAPHICS),GRAPHICS_API_OPENGL_ES2)
+ # LDLIBS += -lGLESv2 -lEGL
+ #endif
+endif
+ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_WIN32)
+ # Libraries for Windows desktop compilation
+ LDFLAGS += -L..\src
+ LDLIBS = -lraylib -lgdi32 -lwinmm -lshcore
+ ifneq ($(GRAPHICS),GRAPHICS_API_OPENGL_11_SOFTWARE)
+ LDLIBS += -lopengl32
+ endif
endif
ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW))
# Libraries for web (HTML5) compiling
@@ -513,12 +525,13 @@ CORE = \
core/core_basic_screen_manager \
core/core_basic_window \
core/core_clipboard_text \
+ core/core_compute_hash \
core/core_custom_frame_control \
core/core_custom_logging \
core/core_delta_time \
core/core_directory_files \
core/core_drop_files \
- core/core_high_dpi \
+ core/core_highdpi_demo \
core/core_highdpi_testbed \
core/core_input_actions \
core/core_input_gamepad \
@@ -539,6 +552,7 @@ CORE = \
core/core_storage_values \
core/core_text_file_loading \
core/core_undo_redo \
+ core/core_viewport_scaling \
core/core_vr_simulator \
core/core_window_flags \
core/core_window_letterbox \
@@ -546,6 +560,7 @@ CORE = \
core/core_world_screen
SHAPES = \
+ shapes/shapes_ball_physics \
shapes/shapes_basic_shapes \
shapes/shapes_bouncing_ball \
shapes/shapes_bullet_hell \
@@ -568,11 +583,14 @@ SHAPES = \
shapes/shapes_math_angle_rotation \
shapes/shapes_math_sine_cosine \
shapes/shapes_mouse_trail \
+ shapes/shapes_penrose_tile \
shapes/shapes_pie_chart \
shapes/shapes_rectangle_advanced \
shapes/shapes_rectangle_scaling \
shapes/shapes_recursive_tree \
shapes/shapes_ring_drawing \
+ shapes/shapes_rlgl_color_wheel \
+ shapes/shapes_rlgl_triangle \
shapes/shapes_rounded_rectangle_drawing \
shapes/shapes_simple_particles \
shapes/shapes_splines_drawing \
@@ -585,6 +603,7 @@ TEXTURES = \
textures/textures_background_scrolling \
textures/textures_blend_modes \
textures/textures_bunnymark \
+ textures/textures_cellular_automata \
textures/textures_fog_of_war \
textures/textures_gif_player \
textures/textures_image_channel \
@@ -601,9 +620,11 @@ TEXTURES = \
textures/textures_particles_blending \
textures/textures_polygon_drawing \
textures/textures_raw_data \
+ textures/textures_screen_buffer \
textures/textures_sprite_animation \
textures/textures_sprite_button \
textures/textures_sprite_explosion \
+ textures/textures_sprite_stacking \
textures/textures_srcrec_dstrec \
textures/textures_textured_curve \
textures/textures_tiled_drawing \
@@ -621,6 +642,7 @@ TEXT = \
text/text_input_box \
text/text_rectangle_bounds \
text/text_sprite_fonts \
+ text/text_strings_management \
text/text_unicode_emojis \
text/text_unicode_ranges \
text/text_words_alignment \
@@ -635,6 +657,7 @@ MODELS = \
models/models_box_collisions \
models/models_cubicmap_rendering \
models/models_decals \
+ models/models_directional_billboard \
models/models_first_person_maze \
models/models_geometric_shapes \
models/models_heightmap_rendering \
@@ -665,6 +688,7 @@ SHADERS = \
shaders/shaders_depth_writing \
shaders/shaders_eratosthenes_sieve \
shaders/shaders_fog_rendering \
+ shaders/shaders_game_of_life \
shaders/shaders_hot_reloading \
shaders/shaders_hybrid_rendering \
shaders/shaders_julia_set \
@@ -696,6 +720,7 @@ AUDIO = \
audio/audio_sound_loading \
audio/audio_sound_multi \
audio/audio_sound_positioning \
+ audio/audio_spectrum_visualizer \
audio/audio_stream_effects
OTHERS = \
diff --git a/examples/Makefile.Android b/examples/Makefile.Android
index c00da171e..cf4ad1257 100644
--- a/examples/Makefile.Android
+++ b/examples/Makefile.Android
@@ -130,7 +130,7 @@ ifeq ($(ANDROID_ARCH),ARM)
CFLAGS = -std=c99 -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16
endif
ifeq ($(ANDROID_ARCH),ARM64)
- CFLAGS = -std=c99 -target aarch64 -mfix-cortex-a53-835769
+ CFLAGS = -std=c99 -mfix-cortex-a53-835769
endif
# Compilation functions attributes options
CFLAGS += -ffunction-sections -funwind-tables -fstack-protector-strong -fPIC
diff --git a/examples/Makefile.Web b/examples/Makefile.Web
index 07f8d8fb8..f36113f15 100644
--- a/examples/Makefile.Web
+++ b/examples/Makefile.Web
@@ -513,12 +513,13 @@ CORE = \
core/core_basic_screen_manager \
core/core_basic_window \
core/core_clipboard_text \
+ core/core_compute_hash \
core/core_custom_frame_control \
core/core_custom_logging \
core/core_delta_time \
core/core_directory_files \
core/core_drop_files \
- core/core_high_dpi \
+ core/core_highdpi_demo \
core/core_highdpi_testbed \
core/core_input_actions \
core/core_input_gamepad \
@@ -539,6 +540,7 @@ CORE = \
core/core_storage_values \
core/core_text_file_loading \
core/core_undo_redo \
+ core/core_viewport_scaling \
core/core_vr_simulator \
core/core_window_flags \
core/core_window_letterbox \
@@ -546,6 +548,7 @@ CORE = \
core/core_world_screen
SHAPES = \
+ shapes/shapes_ball_physics \
shapes/shapes_basic_shapes \
shapes/shapes_bouncing_ball \
shapes/shapes_bullet_hell \
@@ -568,11 +571,14 @@ SHAPES = \
shapes/shapes_math_angle_rotation \
shapes/shapes_math_sine_cosine \
shapes/shapes_mouse_trail \
+ shapes/shapes_penrose_tile \
shapes/shapes_pie_chart \
shapes/shapes_rectangle_advanced \
shapes/shapes_rectangle_scaling \
shapes/shapes_recursive_tree \
shapes/shapes_ring_drawing \
+ shapes/shapes_rlgl_color_wheel \
+ shapes/shapes_rlgl_triangle \
shapes/shapes_rounded_rectangle_drawing \
shapes/shapes_simple_particles \
shapes/shapes_splines_drawing \
@@ -585,6 +591,7 @@ TEXTURES = \
textures/textures_background_scrolling \
textures/textures_blend_modes \
textures/textures_bunnymark \
+ textures/textures_cellular_automata \
textures/textures_fog_of_war \
textures/textures_gif_player \
textures/textures_image_channel \
@@ -601,9 +608,11 @@ TEXTURES = \
textures/textures_particles_blending \
textures/textures_polygon_drawing \
textures/textures_raw_data \
+ textures/textures_screen_buffer \
textures/textures_sprite_animation \
textures/textures_sprite_button \
textures/textures_sprite_explosion \
+ textures/textures_sprite_stacking \
textures/textures_srcrec_dstrec \
textures/textures_textured_curve \
textures/textures_tiled_drawing \
@@ -621,6 +630,7 @@ TEXT = \
text/text_input_box \
text/text_rectangle_bounds \
text/text_sprite_fonts \
+ text/text_strings_management \
text/text_unicode_emojis \
text/text_unicode_ranges \
text/text_words_alignment \
@@ -635,6 +645,7 @@ MODELS = \
models/models_box_collisions \
models/models_cubicmap_rendering \
models/models_decals \
+ models/models_directional_billboard \
models/models_first_person_maze \
models/models_geometric_shapes \
models/models_heightmap_rendering \
@@ -665,6 +676,7 @@ SHADERS = \
shaders/shaders_depth_writing \
shaders/shaders_eratosthenes_sieve \
shaders/shaders_fog_rendering \
+ shaders/shaders_game_of_life \
shaders/shaders_hot_reloading \
shaders/shaders_hybrid_rendering \
shaders/shaders_julia_set \
@@ -696,6 +708,7 @@ AUDIO = \
audio/audio_sound_loading \
audio/audio_sound_multi \
audio/audio_sound_positioning \
+ audio/audio_spectrum_visualizer \
audio/audio_stream_effects
# Default target entry
@@ -752,6 +765,9 @@ core/core_basic_window: core/core_basic_window.c
core/core_clipboard_text: core/core_clipboard_text.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
+core/core_compute_hash: core/core_compute_hash.c
+ $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
+
core/core_custom_frame_control: core/core_custom_frame_control.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
@@ -767,7 +783,7 @@ core/core_directory_files: core/core_directory_files.c
core/core_drop_files: core/core_drop_files.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
-core/core_high_dpi: core/core_high_dpi.c
+core/core_highdpi_demo: core/core_highdpi_demo.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
core/core_highdpi_testbed: core/core_highdpi_testbed.c
@@ -833,6 +849,9 @@ core/core_text_file_loading: core/core_text_file_loading.c
core/core_undo_redo: core/core_undo_redo.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
+core/core_viewport_scaling: core/core_viewport_scaling.c
+ $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
+
core/core_vr_simulator: core/core_vr_simulator.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
--preload-file core/resources/shaders/glsl100/distortion.fs@resources/shaders/glsl100/distortion.fs
@@ -850,6 +869,9 @@ core/core_world_screen: core/core_world_screen.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
# Compile SHAPES examples
+shapes/shapes_ball_physics: shapes/shapes_ball_physics.c
+ $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
+
shapes/shapes_basic_shapes: shapes/shapes_basic_shapes.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
@@ -916,6 +938,9 @@ shapes/shapes_math_sine_cosine: shapes/shapes_math_sine_cosine.c
shapes/shapes_mouse_trail: shapes/shapes_mouse_trail.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
+shapes/shapes_penrose_tile: shapes/shapes_penrose_tile.c
+ $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
+
shapes/shapes_pie_chart: shapes/shapes_pie_chart.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
@@ -931,6 +956,12 @@ shapes/shapes_recursive_tree: shapes/shapes_recursive_tree.c
shapes/shapes_ring_drawing: shapes/shapes_ring_drawing.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
+shapes/shapes_rlgl_color_wheel: shapes/shapes_rlgl_color_wheel.c
+ $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
+
+shapes/shapes_rlgl_triangle: shapes/shapes_rlgl_triangle.c
+ $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
+
shapes/shapes_rounded_rectangle_drawing: shapes/shapes_rounded_rectangle_drawing.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
@@ -966,7 +997,10 @@ textures/textures_blend_modes: textures/textures_blend_modes.c
textures/textures_bunnymark: textures/textures_bunnymark.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
- --preload-file textures/resources/wabbit_alpha.png@resources/wabbit_alpha.png
+ --preload-file textures/resources/raybunny.png@resources/raybunny.png
+
+textures/textures_cellular_automata: textures/textures_cellular_automata.c
+ $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
textures/textures_fog_of_war: textures/textures_fog_of_war.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
@@ -1032,6 +1066,9 @@ textures/textures_raw_data: textures/textures_raw_data.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
--preload-file textures/resources/fudesumi.raw@resources/fudesumi.raw
+textures/textures_screen_buffer: textures/textures_screen_buffer.c
+ $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
+
textures/textures_sprite_animation: textures/textures_sprite_animation.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
--preload-file textures/resources/scarfy.png@resources/scarfy.png
@@ -1046,6 +1083,10 @@ textures/textures_sprite_explosion: textures/textures_sprite_explosion.c
--preload-file textures/resources/boom.wav@resources/boom.wav \
--preload-file textures/resources/explosion.png@resources/explosion.png
+textures/textures_sprite_stacking: textures/textures_sprite_stacking.c
+ $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
+ --preload-file textures/resources/booth.png@resources/booth.png
+
textures/textures_srcrec_dstrec: textures/textures_srcrec_dstrec.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
--preload-file textures/resources/scarfy.png@resources/scarfy.png
@@ -1112,6 +1153,9 @@ text/text_sprite_fonts: text/text_sprite_fonts.c
--preload-file text/resources/sprite_fonts/alpha_beta.png@resources/sprite_fonts/alpha_beta.png \
--preload-file text/resources/sprite_fonts/jupiter_crash.png@resources/sprite_fonts/jupiter_crash.png
+text/text_strings_management: text/text_strings_management.c
+ $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
+
text/text_unicode_emojis: text/text_unicode_emojis.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
--preload-file text/resources/dejavu.fnt@resources/dejavu.fnt \
@@ -1172,6 +1216,10 @@ models/models_decals: models/models_decals.c
--preload-file models/resources/models/obj/character_diffuse.png@resources/models/obj/character_diffuse.png \
--preload-file models/resources/raylib_logo.png@resources/raylib_logo.png
+models/models_directional_billboard: models/models_directional_billboard.c
+ $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
+ --preload-file models/resources/skillbot.png@resources/skillbot.png
+
models/models_first_person_maze: models/models_first_person_maze.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
--preload-file models/resources/cubicmap.png@resources/cubicmap.png \
@@ -1316,6 +1364,19 @@ shaders/shaders_fog_rendering: shaders/shaders_fog_rendering.c
--preload-file shaders/resources/shaders/glsl100/lighting.vs@resources/shaders/glsl100/lighting.vs \
--preload-file shaders/resources/shaders/glsl100/fog.fs@resources/shaders/glsl100/fog.fs
+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/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/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
+
shaders/shaders_hot_reloading: shaders/shaders_hot_reloading.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
--preload-file shaders/resources/shaders/glsl100/reload.fs@resources/shaders/glsl100/reload.fs
@@ -1468,6 +1529,11 @@ audio/audio_sound_positioning: audio/audio_sound_positioning.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
--preload-file audio/resources/coin.wav@resources/coin.wav
+audio/audio_spectrum_visualizer: audio/audio_spectrum_visualizer.c
+ $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
+ --preload-file audio/resources/shaders/glsl100/fft.fs@resources/shaders/glsl100/fft.fs \
+ --preload-file audio/resources/country.mp3@resources/country.mp3
+
audio/audio_stream_effects: audio/audio_stream_effects.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
--preload-file audio/resources/country.mp3@resources/country.mp3
diff --git a/examples/README.md b/examples/README.md
index 4d0b25d56..367bfa0ab 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -17,11 +17,11 @@ 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: 192]
+## EXAMPLES COLLECTION [TOTAL: 205]
-### category: core [45]
+### category: core [47]
-Examples using raylib[core](../src/rcore.c) platform functionality like window creation, inputs, drawing modes and system functionality.
+Examples using raylib [core](../src/rcore.c) module platform functionality: window creation, inputs, drawing modes and system functionality.
| example | image | difficulty
level | version
created | last version
updated | original
developer |
|-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|
@@ -43,7 +43,7 @@ Examples using raylib[core](../src/rcore.c) platform functionality like window c
| [core_3d_camera_free](core/core_3d_camera_free.c) |
| ⭐☆☆☆ | 1.3 | 1.3 | [Ramon Santamaria](https://github.com/raysan5) |
| [core_3d_camera_first_person](core/core_3d_camera_first_person.c) |
| ⭐⭐☆☆ | 1.3 | 1.3 | [Ramon Santamaria](https://github.com/raysan5) |
| [core_3d_camera_split_screen](core/core_3d_camera_split_screen.c) |
| ⭐⭐⭐☆ | 3.7 | 4.0 | [Jeffery Myers](https://github.com/JeffM2501) |
-| [core_3d_camera_fps](core/core_3d_camera_fps.c) |
| ⭐⭐⭐☆ | 5.5 | 5.5 | [Agnis Aldins](https://github.com/nezvers) |
+| [core_3d_camera_fps](core/core_3d_camera_fps.c) |
| ⭐⭐⭐☆ | 5.5 | 5.5 | [Agnis Aldiņš](https://github.com/nezvers) |
| [core_3d_picking](core/core_3d_picking.c) |
| ⭐⭐☆☆ | 1.3 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) |
| [core_world_screen](core/core_world_screen.c) |
| ⭐⭐☆☆ | 1.3 | 1.4 | [Ramon Santamaria](https://github.com/raysan5) |
| [core_window_flags](core/core_window_flags.c) |
| ⭐⭐⭐☆ | 3.5 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) |
@@ -61,17 +61,19 @@ Examples using raylib[core](../src/rcore.c) platform functionality like window c
| [core_smooth_pixelperfect](core/core_smooth_pixelperfect.c) |
| ⭐⭐⭐☆ | 3.7 | 4.0 | [Giancamillo Alessandroni](https://github.com/NotManyIdeasDev) |
| [core_random_sequence](core/core_random_sequence.c) |
| ⭐☆☆☆ | 5.0 | 5.0 | [Dalton Overmyer](https://github.com/REDl3east) |
| [core_automation_events](core/core_automation_events.c) |
| ⭐⭐⭐☆ | 5.0 | 5.0 | [Ramon Santamaria](https://github.com/raysan5) |
-| [core_high_dpi](core/core_high_dpi.c) |
| ⭐⭐☆☆ | 5.0 | 5.5 | [Jonathan Marler](https://github.com/marler8997) |
+| [core_highdpi_demo](core/core_highdpi_demo.c) |
| ⭐⭐☆☆ | 5.0 | 5.5 | [Jonathan Marler](https://github.com/marler8997) |
| [core_render_texture](core/core_render_texture.c) |
| ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) |
| [core_undo_redo](core/core_undo_redo.c) |
| ⭐⭐⭐☆ | 5.5 | 5.6 | [Ramon Santamaria](https://github.com/raysan5) |
+| [core_viewport_scaling](core/core_viewport_scaling.c) |
| ⭐⭐☆☆ | 5.5 | 5.5 | [Agnis Aldiņš](https://github.com/nezvers) |
| [core_input_actions](core/core_input_actions.c) |
| ⭐⭐☆☆ | 5.5 | 5.6 | [Jett](https://github.com/JettMonstersGoBoom) |
| [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 | [Robin](https://github.com/RobinsAviary) |
+| [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_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) |
-### category: shapes [34]
+### category: shapes [38]
Examples using raylib shapes drawing functionality, provided by raylib [shapes](../src/rshapes.c) module.
@@ -111,8 +113,12 @@ Examples using raylib shapes drawing functionality, provided by raylib [shapes](
| [shapes_starfield_effect](shapes/shapes_starfield_effect.c) |
| ⭐⭐☆☆ | 5.5 | 5.6-dev | [JP Mortiboys](https://github.com/themushroompirates) |
| [shapes_lines_drawing](shapes/shapes_lines_drawing.c) |
| ⭐☆☆☆ | 5.6-dev | 5.6 | [Robin](https://github.com/RobinsAviary) |
| [shapes_math_angle_rotation](shapes/shapes_math_angle_rotation.c) |
| ⭐☆☆☆ | 5.6-dev | 5.6 | [Kris](https://github.com/krispy-snacc) |
+| [shapes_rlgl_color_wheel](shapes/shapes_rlgl_color_wheel.c) |
| ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [Robin](https://github.com/RobinsAviary) |
+| [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) |
-### category: textures [26]
+### category: textures [29]
Examples using raylib textures functionality, including image/textures loading/generation and drawing, provided by raylib [textures](../src/rtextures.c) module.
@@ -143,9 +149,12 @@ Examples using raylib textures functionality, including image/textures loading/g
| [textures_image_kernel](textures/textures_image_kernel.c) |
| ⭐⭐⭐⭐️ | 1.3 | 1.3 | [Karim Salem](https://github.com/kimo-s) |
| [textures_image_channel](textures/textures_image_channel.c) |
| ⭐⭐☆☆ | 5.5 | 5.5 | [Bruno Cabral](https://github.com/brccabral) |
| [textures_image_rotate](textures/textures_image_rotate.c) |
| ⭐⭐☆☆ | 1.0 | 1.0 | [Ramon Santamaria](https://github.com/raysan5) |
+| [textures_screen_buffer](textures/textures_screen_buffer.c) |
| ⭐⭐☆☆ | 5.5 | 5.5 | [Agnis Aldiņš](https://github.com/nezvers) |
| [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) |
-### category: text [15]
+### category: text [16]
Examples using raylib text functionality, including sprite fonts loading/generation and text drawing, provided by raylib [text](../src/rtext.c) module.
@@ -166,8 +175,9 @@ Examples using raylib text functionality, including sprite fonts loading/generat
| [text_codepoints_loading](text/text_codepoints_loading.c) |
| ⭐⭐⭐☆ | 4.2 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) |
| [text_inline_styling](text/text_inline_styling.c) |
| ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [Wagner Barongello](https://github.com/SultansOfCode) |
| [text_words_alignment](text/text_words_alignment.c) |
| ⭐☆☆☆ | 5.6-dev | 5.6-dev | [JP Mortiboys](https://github.com/themushroompirates) |
+| [text_strings_management](text/text_strings_management.c) |
| ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [David Buzatto](https://github.com/davidbuzatto) |
-### category: models [26]
+### category: models [27]
Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/rmodels.c) module.
@@ -199,8 +209,9 @@ Examples using raylib models functionality, including models loading/generation
| [models_basic_voxel](models/models_basic_voxel.c) |
| ⭐⭐☆☆ | 5.5 | 5.5 | [Tim Little](https://github.com/timlittle) |
| [models_rotating_cube](models/models_rotating_cube.c) |
| ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Jopestpe](https://github.com/jopestpe) |
| [models_decals](models/models_decals.c) |
| ⭐⭐⭐⭐️ | 5.6-dev | 5.6-dev | [JP Mortiboys](https://github.com/themushroompirates) |
+| [models_directional_billboard](models/models_directional_billboard.c) |
| ⭐⭐☆☆ | 5.6-dev | 5.6 | [Robin](https://github.com/RobinsAviary) |
-### category: shaders [32]
+### category: shaders [33]
Examples using raylib shaders functionality, including shaders loading, parameters configuration and drawing using them (model shaders and postprocessing shaders). This functionality is directly provided by raylib [rlgl](../src/rlgl.c) module.
@@ -238,8 +249,9 @@ Examples using raylib shaders functionality, including shaders loading, paramete
| [shaders_lightmap_rendering](shaders/shaders_lightmap_rendering.c) |
| ⭐⭐⭐☆ | 4.5 | 4.5 | [Jussi Viitala](https://github.com/nullstare) |
| [shaders_rounded_rectangle](shaders/shaders_rounded_rectangle.c) |
| ⭐⭐⭐☆ | 5.5 | 5.5 | [Anstro Pleuton](https://github.com/anstropleuton) |
| [shaders_depth_rendering](shaders/shaders_depth_rendering.c) |
| ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [Luís Almeida](https://github.com/luis605) |
+| [shaders_game_of_life](shaders/shaders_game_of_life.c) |
| ⭐⭐⭐☆ | 5.6 | 5.6 | [Jordi Santonja](https://github.com/JordSant) |
-### category: audio [8]
+### category: audio [9]
Examples using raylib audio functionality, including sound/music loading and playing. This functionality is provided by raylib [raudio](../src/raudio.c) module. Note this module can be used standalone independently of raylib.
@@ -253,6 +265,7 @@ Examples using raylib audio functionality, including sound/music loading and pla
| [audio_stream_effects](audio/audio_stream_effects.c) |
| ⭐⭐⭐⭐️ | 4.2 | 5.0 | [Ramon Santamaria](https://github.com/raysan5) |
| [audio_sound_multi](audio/audio_sound_multi.c) |
| ⭐⭐☆☆ | 5.0 | 5.0 | [Jeffery Myers](https://github.com/JeffM2501) |
| [audio_sound_positioning](audio/audio_sound_positioning.c) |
| ⭐⭐☆☆ | 5.5 | 5.5 | [Le Juez Victor](https://github.com/Bigfoot71) |
+| [audio_spectrum_visualizer](audio/audio_spectrum_visualizer.c) |
| ⭐⭐⭐☆ | 6.0 | 5.6-dev | [IANN](https://github.com/meisei4) |
### category: others [6]
@@ -268,4 +281,4 @@ Examples showing raylib misc functionality that does not fit in other categories
| [web_basic_window](others/web_basic_window.c) |
| ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) |
Some example missing? As always, contributions are welcome, feel free to send new examples!
-Here is an[examples template](examples_template.c) with instructions to start with!
+Here is an [examples template](examples_template.c) with instructions to start with!
diff --git a/examples/audio/audio_music_stream.c b/examples/audio/audio_music_stream.c
index 48be49e42..05ec1c2d6 100644
--- a/examples/audio/audio_music_stream.c
+++ b/examples/audio/audio_music_stream.c
@@ -36,6 +36,12 @@ int main(void)
float timePlayed = 0.0f; // Time played normalized [0.0f..1.0f]
bool pause = false; // Music playing paused
+ float pan = 0.0f; // Default audio pan center [-1.0f..1.0f]
+ SetMusicPan(music, pan);
+
+ float volume = 0.8f; // Default audio volume [0.0f..1.0f]
+ SetMusicVolume(music, volume);
+
SetTargetFPS(30); // Set our game to run at 30 frames-per-second
//--------------------------------------------------------------------------------------
@@ -62,6 +68,34 @@ int main(void)
else ResumeMusicStream(music);
}
+ // Set audio pan
+ if (IsKeyDown(KEY_LEFT))
+ {
+ pan -= 0.05f;
+ if (pan < -1.0f) pan = -1.0f;
+ SetMusicPan(music, pan);
+ }
+ else if (IsKeyDown(KEY_RIGHT))
+ {
+ pan += 0.05f;
+ if (pan > 1.0f) pan = 1.0f;
+ SetMusicPan(music, pan);
+ }
+
+ // Set audio volume
+ if (IsKeyDown(KEY_DOWN))
+ {
+ volume -= 0.05f;
+ if (volume < 0.0f) volume = 0.0f;
+ SetMusicVolume(music, volume);
+ }
+ else if (IsKeyDown(KEY_UP))
+ {
+ volume += 0.05f;
+ if (volume > 1.0f) volume = 1.0f;
+ SetMusicVolume(music, volume);
+ }
+
// Get normalized time played for current music stream
timePlayed = GetMusicTimePlayed(music)/GetMusicTimeLength(music);
@@ -76,6 +110,11 @@ int main(void)
DrawText("MUSIC SHOULD BE PLAYING!", 255, 150, 20, LIGHTGRAY);
+ 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(200, 200, 400, 12, LIGHTGRAY);
DrawRectangle(200, 200, (int)(timePlayed*400.0f), 12, MAROON);
DrawRectangleLines(200, 200, 400, 12, GRAY);
@@ -83,6 +122,11 @@ int main(void)
DrawText("PRESS SPACE TO RESTART MUSIC", 215, 250, 20, LIGHTGRAY);
DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 280, 20, LIGHTGRAY);
+ 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);
+
EndDrawing();
//----------------------------------------------------------------------------------
}
diff --git a/examples/audio/audio_raw_stream.c b/examples/audio/audio_raw_stream.c
index 4deae2090..b327e92af 100644
--- a/examples/audio/audio_raw_stream.c
+++ b/examples/audio/audio_raw_stream.c
@@ -166,7 +166,7 @@ int main(void)
memcpy(writeBuf + writeCursor, data + readCursor, writeLength*sizeof(short));
// Update cursors and loop audio
- readCursor = (readCursor + writeLength) % waveLength;
+ readCursor = (readCursor + writeLength)%waveLength;
writeCursor += writeLength;
}
diff --git a/examples/audio/audio_sound_positioning.c b/examples/audio/audio_sound_positioning.c
index 4d29954f6..34b15c07b 100644
--- a/examples/audio/audio_sound_positioning.c
+++ b/examples/audio/audio_sound_positioning.c
@@ -69,6 +69,7 @@ int main(void)
};
SetSoundPosition(camera, sound, spherePos, 20.0f);
+
if (!IsSoundPlaying(sound)) PlaySound(sound);
//----------------------------------------------------------------------------------
@@ -94,6 +95,8 @@ int main(void)
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
+
+ return 0;
}
//------------------------------------------------------------------------------------
diff --git a/examples/audio/audio_spectrum_visualizer.c b/examples/audio/audio_spectrum_visualizer.c
new file mode 100644
index 000000000..f5334c9cc
--- /dev/null
+++ b/examples/audio/audio_spectrum_visualizer.c
@@ -0,0 +1,285 @@
+/*******************************************************************************************
+*
+* raylib [audio] example - spectrum visualizer
+*
+* Example complexity rating: [★★★☆] 3/4
+*
+* Example originally created with raylib 6.0, last time updated with raylib 5.6-dev
+*
+* Inspired by Inigo Quilez's https://www.shadertoy.com/
+* Resources/specification: https://gist.github.com/soulthreads/2efe50da4be1fb5f7ab60ff14ca434b8
+*
+* Example created by created by IANN (@meisei4) 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 IANN (@meisei4)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"
+
+#include
+#include
+#include
+
+#if defined(PLATFORM_DESKTOP)
+ #define GLSL_VERSION 330
+#else // PLATFORM_ANDROID, PLATFORM_WEB
+ #define GLSL_VERSION 100
+#endif
+
+#define MONO 1
+#define SAMPLE_RATE 44100
+#define SAMPLE_RATE_F 44100.0f
+#define FFT_WINDOW_SIZE 1024
+#define BUFFER_SIZE 512
+#define PER_SAMPLE_BIT_DEPTH 16
+#define AUDIO_STREAM_RING_BUFFER_SIZE (FFT_WINDOW_SIZE*2)
+#define EFFECTIVE_SAMPLE_RATE (SAMPLE_RATE_F*0.5f)
+#define WINDOW_TIME ((double)FFT_WINDOW_SIZE/(double)EFFECTIVE_SAMPLE_RATE)
+#define FFT_HISTORICAL_SMOOTHING_DUR 2.0f
+#define MIN_DECIBELS (-100.0f) // https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/minDecibels
+#define MAX_DECIBELS (-30.0f) // https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/maxDecibels
+#define INVERSE_DECIBEL_RANGE (1.0f/(MAX_DECIBELS - MIN_DECIBELS))
+#define DB_TO_LINEAR_SCALE (20.0f/2.302585092994046f)
+#define SMOOTHING_TIME_CONSTANT 0.8f // https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/smoothingTimeConstant
+#define TEXTURE_HEIGHT 1
+#define FFT_ROW 0
+#define UNUSED_CHANNEL 0.0f
+
+typedef struct FFTComplex { float real, imaginary; } FFTComplex;
+
+typedef struct FFTData {
+ FFTComplex *spectrum;
+ FFTComplex *workBuffer;
+ float *prevMagnitudes;
+ float (*fftHistory)[BUFFER_SIZE];
+ int fftHistoryLen;
+ int historyPos;
+ double lastFftTime;
+ float tapbackPos;
+} FFTData;
+
+static void CaptureFrame(FFTData *fftData, const float *audioSamples);
+static void RenderFrame(const FFTData *fftData, Image *fftImage);
+static void CooleyTukeyFFTSlow(FFTComplex *spectrum, int n);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //----------------------------------------------------------------------------------- ---
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [audio] example - spectrum visualizer");
+
+ Image fftImage = GenImageColor(BUFFER_SIZE, TEXTURE_HEIGHT, WHITE);
+ Texture2D fftTexture = LoadTextureFromImage(fftImage);
+ RenderTexture2D bufferA = LoadRenderTexture(screenWidth, screenHeight);
+ Vector2 iResolution = { (float)screenWidth, (float)screenHeight };
+
+ Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/fft.fs", GLSL_VERSION));
+
+ int iResolutionLocation = GetShaderLocation(shader, "iResolution");
+ int iChannel0Location = GetShaderLocation(shader, "iChannel0");
+ SetShaderValue(shader, iResolutionLocation, &iResolution, SHADER_UNIFORM_VEC2);
+ SetShaderValueTexture(shader, iChannel0Location, fftTexture);
+
+ InitAudioDevice();
+ SetAudioStreamBufferSizeDefault(AUDIO_STREAM_RING_BUFFER_SIZE);
+
+ // WARNING: Memory out-of-bounds on PLATFORM_WEB
+ Wave wav = LoadWave("resources/country.mp3");
+ WaveFormat(&wav, SAMPLE_RATE, PER_SAMPLE_BIT_DEPTH, MONO);
+
+ AudioStream audioStream = LoadAudioStream(SAMPLE_RATE, PER_SAMPLE_BIT_DEPTH, MONO);
+ PlayAudioStream(audioStream);
+
+ int fftHistoryLen = (int)ceilf(FFT_HISTORICAL_SMOOTHING_DUR/WINDOW_TIME) + 1;
+
+ FFTData fft = {
+ .spectrum = RL_CALLOC(sizeof(FFTComplex), FFT_WINDOW_SIZE),
+ .workBuffer = RL_CALLOC(sizeof(FFTComplex), FFT_WINDOW_SIZE),
+ .prevMagnitudes = RL_CALLOC(BUFFER_SIZE, sizeof(float)),
+ .fftHistory = RL_CALLOC(fftHistoryLen, sizeof(float[BUFFER_SIZE])),
+ .fftHistoryLen = fftHistoryLen,
+ .historyPos = 0,
+ .lastFftTime = 0.0,
+ .tapbackPos = 0.01f
+ };
+
+ size_t wavCursor = 0;
+ const short *wavPCM16 = wav.data;
+
+ short chunkSamples[AUDIO_STREAM_RING_BUFFER_SIZE] = { 0 };
+ float audioSamples[FFT_WINDOW_SIZE] = { 0 };
+
+ SetTargetFPS(60);
+ //----------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ while (IsAudioStreamProcessed(audioStream))
+ {
+ for (int i = 0; i < AUDIO_STREAM_RING_BUFFER_SIZE; i++)
+ {
+ int left = (wav.channels == 2)? wavPCM16[wavCursor*2 + 0] : wavPCM16[wavCursor];
+ int right = (wav.channels == 2)? wavPCM16[wavCursor*2 + 1] : left;
+ chunkSamples[i] = (short)((left + right)/2);
+
+ if (++wavCursor >= wav.frameCount) wavCursor = 0;
+ }
+
+ UpdateAudioStream(audioStream, chunkSamples, AUDIO_STREAM_RING_BUFFER_SIZE);
+
+ for (int i = 0; i < FFT_WINDOW_SIZE; i++) audioSamples[i] = (chunkSamples[i*2] + chunkSamples[i*2 + 1])*0.5f/32767.0f;
+ }
+
+ CaptureFrame(&fft, audioSamples);
+ RenderFrame(&fft, &fftImage);
+ UpdateTexture(fftTexture, fftImage.data);
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ BeginShaderMode(shader);
+ SetShaderValueTexture(shader, iChannel0Location, fftTexture);
+ DrawTextureRec(bufferA.texture,
+ (Rectangle){ 0, 0, (float)screenWidth, (float)-screenHeight },
+ (Vector2){ 0, 0 }, WHITE);
+ EndShaderMode();
+
+ EndDrawing();
+ //------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ UnloadShader(shader);
+ UnloadRenderTexture(bufferA);
+ UnloadTexture(fftTexture);
+ UnloadImage(fftImage);
+ UnloadAudioStream(audioStream);
+ UnloadWave(wav);
+ CloseAudioDevice();
+
+ RL_FREE(fft.spectrum);
+ RL_FREE(fft.workBuffer);
+ RL_FREE(fft.prevMagnitudes);
+ RL_FREE(fft.fftHistory);
+
+ CloseWindow(); // Close window and OpenGL context
+ //----------------------------------------------------------------------------------
+
+ return 0;
+}
+
+// Cooley–Tukey FFT https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm#Data_reordering,_bit_reversal,_and_in-place_algorithms
+static void CooleyTukeyFFTSlow(FFTComplex *spectrum, int n)
+{
+ int j = 0;
+ for (int i = 1; i < n - 1; i++)
+ {
+ int bit = n >> 1;
+ while (j >= bit)
+ {
+ j -= bit;
+ bit >>= 1;
+ }
+ j += bit;
+ if (i < j)
+ {
+ FFTComplex temp = spectrum[i];
+ spectrum[i] = spectrum[j];
+ spectrum[j] = temp;
+ }
+ }
+
+ for (int len = 2; len <= n; len <<= 1)
+ {
+ float angle = -2.0f*PI/len;
+ FFTComplex twiddleUnit = { cosf(angle), sinf(angle) };
+ for (int i = 0; i < n; i += len)
+ {
+ FFTComplex twiddleCurrent = { 1.0f, 0.0f };
+ for (int j = 0; j < len/2; j++)
+ {
+ FFTComplex even = spectrum[i + j];
+ FFTComplex odd = spectrum[i + j + len/2];
+ FFTComplex twiddledOdd = {
+ odd.real*twiddleCurrent.real - odd.imaginary*twiddleCurrent.imaginary,
+ odd.real*twiddleCurrent.imaginary + odd.imaginary*twiddleCurrent.real
+ };
+
+ spectrum[i + j].real = even.real + twiddledOdd.real;
+ spectrum[i + j].imaginary = even.imaginary + twiddledOdd.imaginary;
+ spectrum[i + j + len/2].real = even.real - twiddledOdd.real;
+ spectrum[i + j + len/2].imaginary = even.imaginary - twiddledOdd.imaginary;
+
+ float twiddleRealNext = twiddleCurrent.real*twiddleUnit.real - twiddleCurrent.imaginary*twiddleUnit.imaginary;
+ twiddleCurrent.imaginary = twiddleCurrent.real*twiddleUnit.imaginary + twiddleCurrent.imaginary*twiddleUnit.real;
+ twiddleCurrent.real = twiddleRealNext;
+ }
+ }
+ }
+}
+
+static void CaptureFrame(FFTData *fftData, const float *audioSamples)
+{
+ for (int i = 0; i < FFT_WINDOW_SIZE; i++)
+ {
+ float x = (2.0f*PI*i)/(FFT_WINDOW_SIZE - 1.0f);
+ float blackmanWeight = 0.42f - 0.5f*cosf(x) + 0.08f*cosf(2.0f*x); // https://en.wikipedia.org/wiki/Window_function#Blackman_window
+ fftData->workBuffer[i].real = audioSamples[i]*blackmanWeight;
+ fftData->workBuffer[i].imaginary = 0.0f;
+ }
+
+ CooleyTukeyFFTSlow(fftData->workBuffer, FFT_WINDOW_SIZE);
+ memcpy(fftData->spectrum, fftData->workBuffer, sizeof(FFTComplex)*FFT_WINDOW_SIZE);
+
+ float smoothedSpectrum[BUFFER_SIZE];
+
+ for (int bin = 0; bin < BUFFER_SIZE; bin++)
+ {
+ float re = fftData->workBuffer[bin].real;
+ float im = fftData->workBuffer[bin].imaginary;
+ float linearMagnitude = sqrtf(re*re + im*im)/FFT_WINDOW_SIZE;
+
+ float smoothedMagnitude = SMOOTHING_TIME_CONSTANT*fftData->prevMagnitudes[bin] + (1.0f - SMOOTHING_TIME_CONSTANT)*linearMagnitude;
+ fftData->prevMagnitudes[bin] = smoothedMagnitude;
+
+ float db = logf(fmaxf(smoothedMagnitude, 1e-40f))*DB_TO_LINEAR_SCALE;
+ float normalized = (db - MIN_DECIBELS)*INVERSE_DECIBEL_RANGE;
+ smoothedSpectrum[bin] = Clamp(normalized, 0.0f, 1.0f);
+ }
+
+ fftData->lastFftTime = GetTime();
+ memcpy(fftData->fftHistory[fftData->historyPos], smoothedSpectrum, sizeof(smoothedSpectrum));
+ fftData->historyPos = (fftData->historyPos + 1)%fftData->fftHistoryLen;
+}
+
+static void RenderFrame(const FFTData *fftData, Image *fftImage)
+{
+ double framesSinceTapback = floor(fftData->tapbackPos/WINDOW_TIME);
+ framesSinceTapback = Clamp(framesSinceTapback, 0.0, fftData->fftHistoryLen - 1);
+
+ int historyPosition = (fftData->historyPos - 1 - (int)framesSinceTapback)%fftData->fftHistoryLen;
+ if (historyPosition < 0) historyPosition += fftData->fftHistoryLen;
+
+ const float *amplitude = fftData->fftHistory[historyPosition];
+ for (int bin = 0; bin < BUFFER_SIZE; bin++) ImageDrawPixel(fftImage, bin, FFT_ROW, ColorFromNormalized((Vector4){ amplitude[bin], UNUSED_CHANNEL, UNUSED_CHANNEL, UNUSED_CHANNEL }));
+}
\ No newline at end of file
diff --git a/examples/audio/audio_spectrum_visualizer.png b/examples/audio/audio_spectrum_visualizer.png
new file mode 100644
index 000000000..c3f1bc8b0
Binary files /dev/null and b/examples/audio/audio_spectrum_visualizer.png differ
diff --git a/examples/audio/resources/shaders/glsl100/fft.fs b/examples/audio/resources/shaders/glsl100/fft.fs
new file mode 100644
index 000000000..a97bf336b
--- /dev/null
+++ b/examples/audio/resources/shaders/glsl100/fft.fs
@@ -0,0 +1,37 @@
+#version 100
+
+precision mediump float;
+
+// Input vertex attributes (from vertex shader)
+varying vec2 fragTexCoord;
+varying vec4 fragColor;
+
+// Input uniform values
+uniform vec2 iResolution;
+uniform sampler2D iChannel0;
+
+const vec4 BLACK = vec4(0.0, 0.0, 0.0, 1.0);
+const vec4 WHITE = vec4(1.0, 1.0, 1.0, 1.0);
+const float FFT_ROW = 0.0;
+const float NUM_OF_BINS = 512.0;
+
+void main()
+{
+ vec2 fragCoord = fragTexCoord*iResolution;
+ float cellWidth = iResolution.x/NUM_OF_BINS;
+ float binIndex = floor(fragCoord.x/cellWidth);
+ float localX = mod(fragCoord.x, cellWidth);
+ float barWidth = cellWidth - 1.0;
+ vec4 color = WHITE;
+
+ if (localX <= barWidth)
+ {
+ float sampleX = (binIndex + 0.5)/NUM_OF_BINS;
+ vec2 sampleCoord = vec2(sampleX, FFT_ROW);
+ float amplitude = texture2D(iChannel0, sampleCoord).r; // Only filled the red channel, all channels left open for alternative use
+
+ if (fragTexCoord.y < amplitude) color = BLACK;
+ }
+
+ gl_FragColor = color;
+}
diff --git a/examples/audio/resources/shaders/glsl120/fft.fs b/examples/audio/resources/shaders/glsl120/fft.fs
new file mode 100644
index 000000000..bab5d533b
--- /dev/null
+++ b/examples/audio/resources/shaders/glsl120/fft.fs
@@ -0,0 +1,35 @@
+#version 120
+
+// Input vertex attributes (from vertex shader)
+varying vec2 fragTexCoord;
+varying vec4 fragColor;
+
+// Input uniform values
+uniform vec2 iResolution;
+uniform sampler2D iChannel0;
+
+const vec4 BLACK = vec4(0.0, 0.0, 0.0, 1.0);
+const vec4 WHITE = vec4(1.0, 1.0, 1.0, 1.0);
+const float FFT_ROW = 0.0;
+const float NUM_OF_BINS = 512.0;
+
+void main()
+{
+ vec2 fragCoord = fragTexCoord*iResolution;
+ float cellWidth = iResolution.x/NUM_OF_BINS;
+ float binIndex = floor(fragCoord.x/cellWidth);
+ float localX = mod(fragCoord.x, cellWidth);
+ float barWidth = cellWidth - 1.0;
+ vec4 color = WHITE;
+
+ if (localX <= barWidth)
+ {
+ float sampleX = (binIndex + 0.5)/NUM_OF_BINS;
+ vec2 sampleCoord = vec2(sampleX, FFT_ROW);
+ float amplitude = texture2D(iChannel0, sampleCoord).r; // Only filled the red channel, all channels left open for alternative use
+
+ if (fragTexCoord.y < amplitude) color = BLACK;
+ }
+
+ gl_FragColor = color;
+}
diff --git a/examples/audio/resources/shaders/glsl330/fft.fs b/examples/audio/resources/shaders/glsl330/fft.fs
new file mode 100644
index 000000000..20b2e9dfa
--- /dev/null
+++ b/examples/audio/resources/shaders/glsl330/fft.fs
@@ -0,0 +1,35 @@
+#version 330
+
+in vec2 fragTexCoord;
+in vec4 fragColor;
+
+out vec4 finalColor;
+
+uniform vec2 iResolution;
+uniform sampler2D iChannel0;
+
+const vec4 BLACK = vec4(0.0, 0.0, 0.0, 1.0);
+const vec4 WHITE = vec4(1.0, 1.0, 1.0, 1.0);
+const float FFT_ROW = 0.0;
+const float NUM_OF_BINS = 512.0;
+
+void main()
+{
+ vec2 fragCoord = fragTexCoord*iResolution;
+ float cellWidth = iResolution.x/NUM_OF_BINS;
+ float binIndex = floor(fragCoord.x/cellWidth);
+ float localX = mod(fragCoord.x, cellWidth);
+ float barWidth = cellWidth - 1.0;
+ vec4 color = WHITE;
+
+ if (localX <= barWidth)
+ {
+ float sampleX = (binIndex + 0.5)/NUM_OF_BINS;
+ vec2 sampleCoord = vec2(sampleX, FFT_ROW);
+ float amplitude = texture(iChannel0, sampleCoord).r; // Only filled the red channel, all channels left open for alternative use
+
+ if (fragTexCoord.y < amplitude) color = BLACK;
+ }
+
+ finalColor = color;
+}
diff --git a/examples/core/core_2d_camera.c b/examples/core/core_2d_camera.c
index b752a9640..7e5a14a70 100644
--- a/examples/core/core_2d_camera.c
+++ b/examples/core/core_2d_camera.c
@@ -125,8 +125,8 @@ int main(void)
DrawRectangle( 10, 10, 250, 113, Fade(SKYBLUE, 0.5f));
DrawRectangleLines( 10, 10, 250, 113, BLUE);
- DrawText("Free 2d camera controls:", 20, 20, 10, BLACK);
- DrawText("- Right/Left to move Offset", 40, 40, 10, DARKGRAY);
+ DrawText("Free 2D camera controls:", 20, 20, 10, BLACK);
+ DrawText("- Right/Left to move player", 40, 40, 10, DARKGRAY);
DrawText("- Mouse Wheel to Zoom in-out", 40, 60, 10, DARKGRAY);
DrawText("- A / S to Rotate", 40, 80, 10, DARKGRAY);
DrawText("- R to reset Zoom and Rotation", 40, 100, 10, DARKGRAY);
diff --git a/examples/core/core_2d_camera.png b/examples/core/core_2d_camera.png
index d2f9e634f..a0fdabb49 100644
Binary files a/examples/core/core_2d_camera.png and b/examples/core/core_2d_camera.png differ
diff --git a/examples/core/core_2d_camera_mouse_zoom.c b/examples/core/core_2d_camera_mouse_zoom.c
index 6cb6dcfa2..9006afbe1 100644
--- a/examples/core/core_2d_camera_mouse_zoom.c
+++ b/examples/core/core_2d_camera_mouse_zoom.c
@@ -23,7 +23,7 @@
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
-int main ()
+int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
@@ -35,9 +35,9 @@ int main ()
Camera2D camera = { 0 };
camera.zoom = 1.0f;
- int zoomMode = 0; // 0-Mouse Wheel, 1-Mouse Move
+ int zoomMode = 0; // 0-Mouse Wheel, 1-Mouse Move
- SetTargetFPS(60); // Set our game to run at 60 frames-per-second
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
// Main game loop
@@ -93,6 +93,7 @@ int main ()
// under the cursor to the screen space point under the cursor at any zoom
camera.target = mouseWorldPos;
}
+
if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT))
{
// Zoom increment
@@ -110,7 +111,6 @@ int main ()
ClearBackground(RAYWHITE);
BeginMode2D(camera);
-
// Draw the 3d grid, rotated 90 degrees and centered around 0,0
// just so we have something in the XY plane
rlPushMatrix();
@@ -121,7 +121,6 @@ int main ()
// Draw a reference circle
DrawCircle(GetScreenWidth()/2, GetScreenHeight()/2, 50, MAROON);
-
EndMode2D();
// Draw mouse reference
@@ -142,5 +141,6 @@ int main ()
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
+
return 0;
}
diff --git a/examples/core/core_3d_camera_fps.c b/examples/core/core_3d_camera_fps.c
index 7aa79c174..ef36e912f 100644
--- a/examples/core/core_3d_camera_fps.c
+++ b/examples/core/core_3d_camera_fps.c
@@ -6,12 +6,12 @@
*
* Example originally created with raylib 5.5, last time updated with raylib 5.5
*
-* Example contributed by Agnis Aldins (@nezvers) and reviewed by Ramon Santamaria (@raysan5)
+* Example contributed by Agnis Aldiņš (@nezvers) 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 Agnis Aldins (@nezvers)
+* Copyright (c) 2025 Agnis Aldiņš (@nezvers)
*
********************************************************************************************/
diff --git a/examples/core/core_clipboard_text.c b/examples/core/core_clipboard_text.c
index 2be64154b..59de6509c 100644
--- a/examples/core/core_clipboard_text.c
+++ b/examples/core/core_clipboard_text.c
@@ -2,22 +2,25 @@
*
* raylib [core] example - clipboard text
*
-* Example complexity rating: [★☆☆☆] 1/4
+* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
*
-* Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5)
+* Example contributed by Ananth S (@Ananth1839) 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 Robin (@RobinsAviary)
+* Copyright (c) 2025 Ananth S (@Ananth1839)
*
********************************************************************************************/
#include "raylib.h"
-#include
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"
+
+#define MAX_TEXT_SAMPLES 5
//------------------------------------------------------------------------------------
// Program main entry point
@@ -31,28 +34,32 @@ int main(void)
InitWindow(screenWidth, screenHeight, "raylib [core] example - clipboard text");
- const char* clipboardText = NULL;
+ // Define some sample texts
+ const char *sampleTexts[MAX_TEXT_SAMPLES] = {
+ "Hello from raylib!",
+ "The quick brown fox jumps over the lazy dog",
+ "Clipboard operations are useful!",
+ "raylib is a simple and easy-to-use library",
+ "Copy and paste me!"
+ };
- // List of text the user can switch through and copy
- const char* copyableText[] = {"raylib is fun", "hello, clipboard!", "potato chips"};
+ const char *clipboardText = NULL;
+ char inputBuffer[256] = "Hello from raylib!"; // Random initial string
- unsigned int textIndex = 0;
+ // UI required variables
+ bool textBoxEditMode = false;
- const char* popupText = NULL;
+ bool btnCutPressed = false;
+ bool btnCopyPressed = false;
+ bool btnPastePressed = false;
+ bool btnClearPressed = false;
+ bool btnRandomPressed = false;
- // Initialize timers
- // The amount of time the pop-up text is on screen, before fading
- const float maxTime = 3.0f;
- float textTimer = 0.0f;
- // The length of time text is offset
- const float animMaxTime = 0.1f;
- float pasteAnim = 0.0f;
- float copyAnim = 0.0f;
- int copyAnimMult = 1;
- float textAnim = 0.0f;
- float textAlpha = 0.0f;
- // Offset amount for animations
- const int offsetAmount = -4;
+ // Set UI style
+ GuiSetStyle(DEFAULT, TEXT_SIZE, 20);
+ GuiSetIconScale(2);
+
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
// Main game loop
@@ -60,95 +67,55 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
- // Check if the user has pressed the copy/paste key combinations
- bool pastePressed = (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_V));
- bool copyPressed = (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_C));
-
- // Update animation timers
- if (textTimer > 0) textTimer -= GetFrameTime();
- if (pasteAnim > 0) pasteAnim -= GetFrameTime();
- if (copyAnim > 0) copyAnim -= GetFrameTime();
- if (textAnim > 0) textAnim -= GetFrameTime();
-
- // React to the user pressing paste
- if (pastePressed)
+ // Handle button interactions
+ if (btnCutPressed)
{
- // Most operating systems hide this information until the user presses Ctrl-V on the window.
+ SetClipboardText(inputBuffer);
+ clipboardText = GetClipboardText();
+ inputBuffer[0] = '\0'; // Quick solution to clear text
+ //memset(inputBuffer, 0, 256); // Clear full buffer properly
+ }
- // Check to see if the clipboard contains an image
- // This function does nothing outside of Windows, as it directly calls the Windows API
- Image image = GetClipboardImage();
-
- if (IsImageValid(image))
+ if (btnCopyPressed)
+ {
+ SetClipboardText(inputBuffer); // Copy text to clipboard
+ clipboardText = GetClipboardText(); // Get text from clipboard
+ }
+
+ if (btnPastePressed)
+ {
+ // Paste text from clipboard
+ clipboardText = GetClipboardText();
+ if (clipboardText != NULL) TextCopy(inputBuffer, clipboardText);
+ }
+
+ if (btnClearPressed)
+ {
+ inputBuffer[0] = '\0'; // Quick solution to clear text
+ //memset(inputBuffer, 0, 256); // Clear full buffer properly
+ }
+
+ if (btnRandomPressed)
+ {
+ // Get random text from sample list
+ TextCopy(inputBuffer, sampleTexts[GetRandomValue(0, MAX_TEXT_SAMPLES - 1)]);
+ }
+
+ // Quick cut/copy/paste with keyboard shortcuts
+ if (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))
+ {
+ if (IsKeyPressed(KEY_X))
{
- // Unload the image
- UnloadImage(image);
- // Update visuals
- popupText = "clipboard contains image";
+ SetClipboardText(inputBuffer);
+ inputBuffer[0] = '\0'; // Quick solution to clear text
}
- else
+
+ if (IsKeyPressed(KEY_C)) SetClipboardText(inputBuffer);
+
+ if (IsKeyPressed(KEY_V))
{
- // Get text from the user's clipboard
clipboardText = GetClipboardText();
-
- // Update visuals
- popupText = "text pasted";
- pasteAnim = animMaxTime;
- }
-
- // Reset animation values
- textTimer = maxTime;
- textAnim = animMaxTime;
- textAlpha = 1;
- }
-
- // React to the user pressing copy
- if (copyPressed)
- {
- // Set the text on the user's clipboard
- SetClipboardText(copyableText[textIndex]);
-
- // Reset values
- textTimer = maxTime;
- textAnim = animMaxTime;
- copyAnim = animMaxTime;
- copyAnimMult = 1;
- textAlpha = 1;
- // Update the text that pops up at the bottom of the screen
- popupText = "text copied";
- }
-
- // Switch to the next item in the list when the user presses up
- if (IsKeyPressed(KEY_UP))
- {
- // Reset animation
- copyAnim = animMaxTime;
- copyAnimMult = 1;
-
- textIndex += 1;
-
- if (textIndex >= sizeof(copyableText) / sizeof(const char*)) // Length of array
- {
- // Loop back to the other end
- textIndex = 0;
- }
- }
-
- // Switch to the previous item in the list when the user presses down
- if (IsKeyPressed(KEY_DOWN))
- {
- // Reset animation
- copyAnim = animMaxTime;
- copyAnimMult = -1;
-
- if (textIndex == 0)
- {
- // Loop back to the other end
- textIndex = (sizeof(copyableText) / sizeof(const char*)) - 1; // Length of array minus one
- }
- else
- {
- textIndex -= 1;
+ if (clipboardText != NULL) TextCopy(inputBuffer, clipboardText);
}
}
//----------------------------------------------------------------------------------
@@ -157,43 +124,32 @@ int main(void)
//----------------------------------------------------------------------------------
BeginDrawing();
- ClearBackground(RAYWHITE);
+ ClearBackground(RAYWHITE);
- // Draw the user's pasted text, if there is any yet
- if (clipboardText)
- {
- // Offset animation
- int offset = 0;
- if (pasteAnim > 0) offset = offsetAmount;
+ // Draw instructions
+ GuiLabel((Rectangle){ 50, 20, 700, 36 }, "Use the BUTTONS or KEY SHORTCUTS:");
+ DrawText("[CTRL+X] - CUT | [CTRL+C] COPY | [CTRL+V] | PASTE", 50, 60, 20, MAROON);
- // Draw the pasted text
- DrawText("pasted clipboard:", 10, 10 + offset, 20, DARKGREEN);
- DrawText(clipboardText, 10, 30 + offset, 20, DARKGRAY);
- }
+ // Draw text box
+ if (GuiTextBox((Rectangle){ 50, 120, 652, 40 }, inputBuffer, 256, textBoxEditMode)) textBoxEditMode = !textBoxEditMode;
- // Offset animation
- int textOffset = 0;
- if (copyAnim > 0) textOffset = offsetAmount;
+ // Random text button
+ btnRandomPressed = GuiButton((Rectangle){ 50 + 652 + 8, 120, 40, 40 }, "#77#");
- // Draw copyable text and controls
- DrawText(copyableText[textIndex], 10, 330 + (textOffset * copyAnimMult), 20, MAROON);
- DrawText("up/down to change string, ctrl-c to copy, ctrl-v to paste", 10, 355, 20, DARKGRAY);
+ // Draw buttons
+ btnCutPressed = GuiButton((Rectangle){ 50, 180, 158, 40 }, "#17#CUT");
+ btnCopyPressed = GuiButton((Rectangle){ 50 + 165, 180, 158, 40 }, "#16#COPY");
+ btnPastePressed = GuiButton((Rectangle){ 50 + 165*2, 180, 158, 40 }, "#18#PASTE");
+ btnClearPressed = GuiButton((Rectangle){ 50 + 165*3, 180, 158, 40 }, "#143#CLEAR");
- // Alpha / Offset animation
- if (textAlpha > 0)
- {
- // Offset animation
- int offset = 0;
- if (textAnim > 0) offset = offsetAmount;
- // Draw pop up text
- DrawText(popupText, 10, 425 + offset, 20, ColorAlpha(DARKGREEN, textAlpha));
-
- // Fade-out animation
- if (textTimer < 0)
- {
- textAlpha -= GetFrameTime();
- }
- }
+ // Draw clipboard status
+ GuiSetState(STATE_DISABLED);
+ GuiLabel((Rectangle){ 50, 260, 700, 40 }, "Clipboard current text data:");
+ GuiSetStyle(TEXTBOX, TEXT_READONLY, 1);
+ GuiTextBox((Rectangle){ 50, 300, 700, 40 }, (char *)clipboardText, 256, false);
+ GuiSetStyle(TEXTBOX, TEXT_READONLY, 0);
+ GuiLabel((Rectangle){ 50, 360, 700, 40 }, "Try copying text from other applications and pasting here!");
+ GuiSetState(STATE_NORMAL);
EndDrawing();
//----------------------------------------------------------------------------------
@@ -205,4 +161,4 @@ int main(void)
//--------------------------------------------------------------------------------------
return 0;
-}
\ No newline at end of file
+}
diff --git a/examples/core/core_clipboard_text.png b/examples/core/core_clipboard_text.png
index caa9b314a..96a7379c7 100644
Binary files a/examples/core/core_clipboard_text.png and b/examples/core/core_clipboard_text.png differ
diff --git a/examples/core/core_compute_hash.c b/examples/core/core_compute_hash.c
new file mode 100644
index 000000000..505ffea7b
--- /dev/null
+++ b/examples/core/core_compute_hash.c
@@ -0,0 +1,143 @@
+/*******************************************************************************************
+*
+* raylib [core] example - compute hash
+*
+* Example complexity rating: [★★☆☆] 2/4
+*
+* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+* 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 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+static char *GetDataAsHexText(const unsigned int *data, int dataSize);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [core] example - compute hash");
+
+ // UI controls variables
+ char textInput[96] = "The quick brown fox jumps over the lazy dog.";
+ bool textBoxEditMode = false;
+ bool btnComputeHashes = false;
+
+ // Data hash values
+ unsigned int hashCRC32 = 0;
+ unsigned int *hashMD5 = NULL;
+ unsigned int *hashSHA1 = NULL;
+ unsigned int *hashSHA256 = NULL;
+
+ // Base64 encoded data
+ char *base64Text = NULL;
+ int base64TextSize = 0;
+
+ SetTargetFPS(60);
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ if (btnComputeHashes)
+ {
+ int textInputLen = strlen(textInput);
+
+ // Encode data to Base64 string (includes NULL terminator), memory must be MemFree()
+ base64Text = EncodeDataBase64((unsigned char *)textInput, textInputLen, &base64TextSize);
+
+ hashCRC32 = ComputeCRC32((unsigned char *)textInput, textInputLen); // Compute CRC32 hash code (4 bytes)
+ hashMD5 = ComputeMD5((unsigned char *)textInput, textInputLen); // Compute MD5 hash code, returns static int[4] (16 bytes)
+ hashSHA1 = ComputeSHA1((unsigned char *)textInput, textInputLen); // Compute SHA1 hash code, returns static int[5] (20 bytes)
+ hashSHA256 = ComputeSHA256((unsigned char *)textInput, textInputLen); // Compute SHA256 hash code, returns static int[8] (32 bytes)
+ }
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ GuiSetStyle(DEFAULT, TEXT_SIZE, 20);
+ GuiSetStyle(DEFAULT, TEXT_SPACING, 2);
+ GuiLabel((Rectangle){ 40, 26, 720, 32 }, "INPUT DATA (TEXT):");
+ GuiSetStyle(DEFAULT, TEXT_SPACING, 1);
+ GuiSetStyle(DEFAULT, TEXT_SIZE, 10);
+
+ if (GuiTextBox((Rectangle){ 40, 64, 720, 32 }, textInput, 95, textBoxEditMode)) textBoxEditMode = !textBoxEditMode;
+
+ btnComputeHashes = GuiButton((Rectangle){ 40, 64 + 40, 720, 32 }, "COMPUTE INPUT DATA HASHES");
+
+ GuiSetStyle(DEFAULT, TEXT_SIZE, 20);
+ GuiSetStyle(DEFAULT, TEXT_SPACING, 2);
+ GuiLabel((Rectangle){ 40, 160, 720, 32 }, "INPUT DATA HASH VALUES:");
+ GuiSetStyle(DEFAULT, TEXT_SPACING, 1);
+ GuiSetStyle(DEFAULT, TEXT_SIZE, 10);
+
+ GuiSetStyle(TEXTBOX, TEXT_READONLY, 1);
+ GuiLabel((Rectangle){ 40, 200, 120, 32 }, "CRC32 [32 bit]:");
+ GuiTextBox((Rectangle){ 40 + 120, 200, 720 - 120, 32 }, GetDataAsHexText(&hashCRC32, 1), 120, false);
+ GuiLabel((Rectangle){ 40, 200 + 36, 120, 32 }, "MD5 [128 bit]:");
+ GuiTextBox((Rectangle){ 40 + 120, 200 + 36, 720 - 120, 32 }, GetDataAsHexText(hashMD5, 4), 120, false);
+ GuiLabel((Rectangle){ 40, 200 + 36*2, 120, 32 }, "SHA1 [160 bit]:");
+ GuiTextBox((Rectangle){ 40 + 120, 200 + 36*2, 720 - 120, 32 }, GetDataAsHexText(hashSHA1, 5), 120, false);
+ GuiLabel((Rectangle){ 40, 200 + 36*3, 120, 32 }, "SHA256 [256 bit]:");
+ GuiTextBox((Rectangle){ 40 + 120, 200 + 36*3, 720 - 120, 32 }, GetDataAsHexText(hashSHA256, 8), 120, false);
+
+ GuiSetState(STATE_FOCUSED);
+ GuiLabel((Rectangle){ 40, 200 + 36*5 - 30, 320, 32 }, "BONUS - BAS64 ENCODED STRING:");
+ GuiSetState(STATE_NORMAL);
+ GuiLabel((Rectangle){ 40, 200 + 36*5, 120, 32 }, "BASE64 ENCODING:");
+ GuiTextBox((Rectangle){ 40 + 120, 200 + 36*5, 720 - 120, 32 }, base64Text, 120, false);
+ GuiSetStyle(TEXTBOX, TEXT_READONLY, 0);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ MemFree(base64Text); // Free Base64 text data
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+static char *GetDataAsHexText(const unsigned int *data, int dataSize)
+{
+ static char text[128] = { 0 };
+ memset(text, 0, 128);
+
+ if ((data != NULL) && (dataSize > 0) && (dataSize < ((128/8) - 1)))
+ {
+ for (int i = 0; i < dataSize; i++) TextCopy(text + i*8, TextFormat("%08X", data[i]));
+ }
+ else TextCopy(text, "00000000");
+
+ return text;
+}
diff --git a/examples/core/core_compute_hash.png b/examples/core/core_compute_hash.png
new file mode 100644
index 000000000..19a5d0b1c
Binary files /dev/null and b/examples/core/core_compute_hash.png differ
diff --git a/examples/core/core_custom_logging.c b/examples/core/core_custom_logging.c
index 5ea2a4762..b1e7ee989 100644
--- a/examples/core/core_custom_logging.c
+++ b/examples/core/core_custom_logging.c
@@ -17,11 +17,11 @@
#include "raylib.h"
-#include // Required for: fopen(), fclose(), fputc(), fwrite(), printf(), fprintf(), funopen()
-#include // Required for: time_t, tm, time(), localtime(), strftime()
+#include // Required for: printf(), vprintf(), fprintf()
+#include // Required for: time_t, tm, time(), localtime(), strftime()
// Custom logging function
-void CustomLog(int msgType, const char *text, va_list args)
+void CustomTraceLog(int msgType, const char *text, va_list args)
{
char timeStr[64] = { 0 };
time_t now = time(NULL);
@@ -54,7 +54,7 @@ int main(void)
const int screenHeight = 450;
// Set custom logger
- SetTraceLogCallback(CustomLog);
+ SetTraceLogCallback(CustomTraceLog);
InitWindow(screenWidth, screenHeight, "raylib [core] example - custom logging");
diff --git a/examples/core/core_delta_time.c b/examples/core/core_delta_time.c
index e52c743da..b77957ed6 100644
--- a/examples/core/core_delta_time.c
+++ b/examples/core/core_delta_time.c
@@ -59,7 +59,7 @@ int main(void)
// GetFrameTime() returns the time it took to draw the last frame, in seconds (usually called delta time)
// Uses the delta time to make the circle look like it's moving at a "consistent" speed regardless of FPS
- // Multiply by 6.0 (an arbitrary value) in order to make the speed
+ // Multiply by 6.0 (an arbitrary value) in order to make the speed
// visually closer to the other circle (at 60 fps), for comparison
deltaCircle.x += GetFrameTime()*6.0f*speed;
// This circle can move faster or slower visually depending on the FPS
@@ -68,7 +68,7 @@ int main(void)
// If either circle is off the screen, reset it back to the start
if (deltaCircle.x > screenWidth) deltaCircle.x = 0;
if (frameCircle.x > screenWidth) frameCircle.x = 0;
-
+
// Reset both circles positions
if (IsKeyPressed(KEY_R))
{
diff --git a/examples/core/core_directory_files.c b/examples/core/core_directory_files.c
index b7e11b686..a98c950f6 100644
--- a/examples/core/core_directory_files.c
+++ b/examples/core/core_directory_files.c
@@ -20,9 +20,7 @@
#define RAYGUI_IMPLEMENTATION
#include "raygui.h" // Required for GUI controls
-#include // Required for: strcpy()
-
-#define MAX_FILEPATH_SIZE 2048
+#define MAX_FILEPATH_SIZE 1024
//------------------------------------------------------------------------------------
// Program main entry point
@@ -53,12 +51,10 @@ int main(void)
//----------------------------------------------------------------------------------
if (btnBackPressed)
{
- strcpy(directory, GetPrevDirectoryPath(directory));
+ TextCopy(directory, GetPrevDirectoryPath(directory));
UnloadDirectoryFiles(files);
files = LoadDirectoryFiles(directory);
}
-
-
//----------------------------------------------------------------------------------
// Draw
@@ -68,19 +64,20 @@ int main(void)
DrawText(directory, 100, 40, 20, DARKGRAY);
- btnBackPressed = GuiButton((Rectangle){ 40.0f, 40.0f, 20, 20 }, "<");
-
+ btnBackPressed = GuiButton((Rectangle){ 40.0f, 38.0f, 48, 24 }, "<");
+
for (int i = 0; i < (int)files.count; i++)
{
Color color = Fade(LIGHTGRAY, 0.3f);
- if (!IsPathFile(files.paths[i]))
+ if (!IsPathFile(files.paths[i]) && DirectoryExists(files.paths[i]))
{
if (GuiButton((Rectangle){0.0f, 85.0f + 40.0f*(float)i, screenWidth, 40}, ""))
{
- strcpy(directory, files.paths[i]);
+ TextCopy(directory, files.paths[i]);
UnloadDirectoryFiles(files);
files = LoadDirectoryFiles(directory);
+ continue;
}
}
diff --git a/examples/core/core_high_dpi.c b/examples/core/core_highdpi_demo.c
similarity index 98%
rename from examples/core/core_high_dpi.c
rename to examples/core/core_highdpi_demo.c
index 312f0c3e2..b1f706d98 100644
--- a/examples/core/core_high_dpi.c
+++ b/examples/core/core_highdpi_demo.c
@@ -1,6 +1,6 @@
/*******************************************************************************************
*
-* raylib [core] example - high dpi
+* raylib [core] example - highdpi demo
*
* Example complexity rating: [★★☆☆] 2/4
*
@@ -33,7 +33,7 @@ int main(void)
const int screenHeight = 450;
SetConfigFlags(FLAG_WINDOW_HIGHDPI | FLAG_WINDOW_RESIZABLE);
- InitWindow(screenWidth, screenHeight, "raylib [core] example - high dpi");
+ InitWindow(screenWidth, screenHeight, "raylib [core] example - highdpi demo");
SetWindowMinSize(450, 450);
int logicalGridDescY = 120;
diff --git a/examples/core/core_high_dpi.png b/examples/core/core_highdpi_demo.png
similarity index 100%
rename from examples/core/core_high_dpi.png
rename to examples/core/core_highdpi_demo.png
diff --git a/examples/core/core_highdpi_testbed.c b/examples/core/core_highdpi_testbed.c
index 5cd2a7dc7..d34951fc0 100644
--- a/examples/core/core_highdpi_testbed.c
+++ b/examples/core/core_highdpi_testbed.c
@@ -27,9 +27,14 @@ int main(void)
const int screenWidth = 800;
const int screenHeight = 450;
+ SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_WINDOW_HIGHDPI);
InitWindow(screenWidth, screenHeight, "raylib [core] example - highdpi testbed");
- // TODO: Load resources / Initialize variables at this point
+ Vector2 scaleDpi = GetWindowScaleDPI();
+ Vector2 mousePos = GetMousePosition();
+ int currentMonitor = GetCurrentMonitor();
+
+ int gridSpacing = 40; // Grid spacing in pixels
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
@@ -39,7 +44,12 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
- // TODO: Update variables / Implement example logic at this point
+ mousePos = GetMousePosition();
+ currentMonitor = GetCurrentMonitor();
+ scaleDpi = GetWindowScaleDPI();
+
+ if (IsKeyPressed(KEY_SPACE)) ToggleBorderlessWindowed();
+ if (IsKeyPressed(KEY_F)) ToggleFullscreen();
//----------------------------------------------------------------------------------
// Draw
@@ -48,11 +58,35 @@ int main(void)
ClearBackground(RAYWHITE);
- // TODO: Draw everything that requires to be drawn at this point
+ // Draw grid
+ 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 < GetScreenWidth()/gridSpacing + 1; v++)
+ {
+ DrawText(TextFormat("%02i", v*gridSpacing), v*gridSpacing - 10, 4, 10, GRAY);
+ DrawLine(v*gridSpacing, 20, v*gridSpacing, GetScreenHeight(), LIGHTGRAY);
+ }
- DrawLineEx((Vector2){ 0, 0 }, (Vector2){ screenWidth, screenHeight }, 2.0f, RED);
- DrawLineEx((Vector2){ 0, screenHeight }, (Vector2){ screenWidth, 0 }, 2.0f, RED);
- DrawText("example base code template", 260, 400, 20, LIGHTGRAY);
+ // 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);
+
+ // Draw reference rectangles, top-left and bottom-right corners
+ DrawRectangle(0, 0, 30, 60, RED);
+ DrawRectangle(GetScreenWidth() - 30, GetScreenHeight() - 60, 30, 60, BLUE);
+
+ // Draw mouse position
+ DrawCircleV(GetMousePosition(), 20, MAROON);
+ DrawRectangle(mousePos.x - 25, mousePos.y, 50, 2, BLACK);
+ DrawRectangle(mousePos.x, mousePos.y - 25, 2, 50, BLACK);
+ DrawText(TextFormat("[%i,%i]", GetMouseX(), GetMouseY()), mousePos.x - 44,
+ (mousePos.y > GetScreenHeight() - 60)? mousePos.y - 46 : mousePos.y + 30, 20, BLACK);
EndDrawing();
//----------------------------------------------------------------------------------
diff --git a/examples/core/core_input_actions.c b/examples/core/core_input_actions.c
index aff356087..f4b1156d8 100644
--- a/examples/core/core_input_actions.c
+++ b/examples/core/core_input_actions.c
@@ -17,7 +17,7 @@
// Simple example for decoding input as actions, allowing remapping of input to different keys or gamepad buttons
// For example instead of using `IsKeyDown(KEY_LEFT)`, you can use `IsActionDown(ACTION_LEFT)`
-// which can be reassigned to e.g. KEY_A and also assigned to a gamepad button. the action will trigger with either gamepad or keys
+// which can be reassigned to e.g. KEY_A and also assigned to a gamepad button. the action will trigger with either gamepad or keys
#include "raylib.h"
@@ -44,7 +44,7 @@ typedef struct ActionInput {
// Global Variables Definition
//----------------------------------------------------------------------------------
static int gamepadIndex = 0; // Gamepad default index
-static ActionInput actionInputs[MAX_ACTION] = { 0 };
+static ActionInput actionInputs[MAX_ACTION] = { 0 };
//----------------------------------------------------------------------------------
// Module Functions Declaration
@@ -67,14 +67,15 @@ int main(void)
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - input actions");
-
- // Set default actions
+
+ // Set default actions
char actionSet = 0;
SetActionsDefault();
+ bool releaseAction = false;
Vector2 position = (Vector2){ 400.0f, 200.0f };
Vector2 size = (Vector2){ 40.0f, 40.0f };
-
+
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
@@ -83,7 +84,8 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
- gamepadIndex = 0; // set this to gamepad being checked
+ gamepadIndex = 0; // Set gamepad being checked
+
if (IsActionDown(ACTION_UP)) position.y -= 2;
if (IsActionDown(ACTION_DOWN)) position.y += 2;
if (IsActionDown(ACTION_LEFT)) position.x -= 2;
@@ -94,7 +96,11 @@ int main(void)
position.y = (screenHeight-size.y)/2;
}
- // Switch control scheme by pressing TAB
+ // Register release action for one frame
+ releaseAction = false;
+ if (IsActionReleased(ACTION_FIRE)) releaseAction = true;
+
+ // Switch control scheme by pressing TAB
if (IsKeyPressed(KEY_TAB))
{
actionSet = !actionSet;
@@ -109,8 +115,8 @@ int main(void)
ClearBackground(GRAY);
- DrawRectangleV(position, size, RED);
-
+ DrawRectangleV(position, size, releaseAction? BLUE : RED);
+
DrawText((actionSet == 0)? "Current input set: WASD (default)" : "Current input set: Cursor", 10, 10, 20, WHITE);
DrawText("Use TAB key to toggles Actions keyset", 10, 50, 20, GREEN);
@@ -134,9 +140,9 @@ int main(void)
static bool IsActionPressed(int action)
{
bool result = false;
-
+
if (action < MAX_ACTION) result = (IsKeyPressed(actionInputs[action].key) || IsGamepadButtonPressed(gamepadIndex, actionInputs[action].button));
-
+
return result;
}
@@ -145,20 +151,20 @@ static bool IsActionPressed(int action)
static bool IsActionReleased(int action)
{
bool result = false;
-
+
if (action < MAX_ACTION) result = (IsKeyReleased(actionInputs[action].key) || IsGamepadButtonReleased(gamepadIndex, actionInputs[action].button));
-
+
return result;
}
// Check action key/button down
// NOTE: Combines key down and gamepad button down in one action
-static bool IsActionDown(int action)
+static bool IsActionDown(int action)
{
bool result = false;
-
+
if (action < MAX_ACTION) result = (IsKeyDown(actionInputs[action].key) || IsGamepadButtonDown(gamepadIndex, actionInputs[action].button));
-
+
return result;
}
diff --git a/examples/core/core_input_gamepad.c b/examples/core/core_input_gamepad.c
index 1da1c4ce0..3c9454318 100644
--- a/examples/core/core_input_gamepad.c
+++ b/examples/core/core_input_gamepad.c
@@ -51,6 +51,8 @@ int main(void)
const float leftTriggerDeadzone = -0.9f;
const float rightTriggerDeadzone = -0.9f;
+ Rectangle vibrateButton = { 0 };
+
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
@@ -61,7 +63,12 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
- // ...
+ if (IsKeyPressed(KEY_LEFT) && gamepad > 0) gamepad--;
+ if (IsKeyPressed(KEY_RIGHT)) gamepad++;
+ Vector2 mousePosition = GetMousePosition();
+
+ vibrateButton = (Rectangle){ 10, 70 + 20*GetGamepadAxisCount(gamepad) + 20, 75, 24 };
+ if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && CheckCollisionPointRec(mousePosition, vibrateButton)) SetGamepadVibration(gamepad, 1.0, 1.0, 1.0);
//----------------------------------------------------------------------------------
// Draw
@@ -70,9 +77,6 @@ int main(void)
ClearBackground(RAYWHITE);
- if (IsKeyPressed(KEY_LEFT) && gamepad > 0) gamepad--;
- if (IsKeyPressed(KEY_RIGHT)) gamepad++;
-
if (IsGamepadAvailable(gamepad))
{
DrawText(TextFormat("GP%d: %s", gamepad, GetGamepadName(gamepad)), 10, 10, 10, BLACK);
@@ -93,7 +97,8 @@ int main(void)
if (leftTrigger < leftTriggerDeadzone) leftTrigger = -1.0f;
if (rightTrigger < rightTriggerDeadzone) rightTrigger = -1.0f;
- if (TextFindIndex(TextToLower(GetGamepadName(gamepad)), XBOX_ALIAS_1) > -1 || TextFindIndex(TextToLower(GetGamepadName(gamepad)), XBOX_ALIAS_2) > -1)
+ if ((TextFindIndex(TextToLower(GetGamepadName(gamepad)), XBOX_ALIAS_1) > -1) ||
+ (TextFindIndex(TextToLower(GetGamepadName(gamepad)), XBOX_ALIAS_2) > -1))
{
DrawTexture(texXboxPad, 0, 0, DARKGRAY);
@@ -125,16 +130,14 @@ int main(void)
if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_THUMB)) leftGamepadColor = RED;
DrawCircle(259, 152, 39, BLACK);
DrawCircle(259, 152, 34, LIGHTGRAY);
- DrawCircle(259 + (int)(leftStickX*20),
- 152 + (int)(leftStickY*20), 25, leftGamepadColor);
+ DrawCircle(259 + (int)(leftStickX*20), 152 + (int)(leftStickY*20), 25, leftGamepadColor);
// Draw axis: right joystick
Color rightGamepadColor = BLACK;
if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_THUMB)) rightGamepadColor = RED;
DrawCircle(461, 237, 38, BLACK);
DrawCircle(461, 237, 33, LIGHTGRAY);
- DrawCircle(461 + (int)(rightStickX*20),
- 237 + (int)(rightStickY*20), 25, rightGamepadColor);
+ DrawCircle(461 + (int)(rightStickX*20), 237 + (int)(rightStickY*20), 25, rightGamepadColor);
// Draw axis: left-right triggers
DrawRectangle(170, 30, 15, 70, GRAY);
@@ -177,16 +180,14 @@ int main(void)
if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_THUMB)) leftGamepadColor = RED;
DrawCircle(319, 255, 35, BLACK);
DrawCircle(319, 255, 31, LIGHTGRAY);
- DrawCircle(319 + (int)(leftStickX*20),
- 255 + (int)(leftStickY*20), 25, leftGamepadColor);
+ DrawCircle(319 + (int)(leftStickX*20), 255 + (int)(leftStickY*20), 25, leftGamepadColor);
// Draw axis: right joystick
Color rightGamepadColor = BLACK;
if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_THUMB)) rightGamepadColor = RED;
DrawCircle(475, 255, 35, BLACK);
DrawCircle(475, 255, 31, LIGHTGRAY);
- DrawCircle(475 + (int)(rightStickX*20),
- 255 + (int)(rightStickY*20), 25, rightGamepadColor);
+ DrawCircle(475 + (int)(rightStickX*20), 255 + (int)(rightStickY*20), 25, rightGamepadColor);
// Draw axis: left-right triggers
DrawRectangle(169, 48, 15, 70, GRAY);
@@ -196,7 +197,6 @@ int main(void)
}
else
{
-
// Draw background: generic
DrawRectangleRounded((Rectangle){ 175, 110, 460, 220}, 0.3f, 16, DARKGRAY);
@@ -237,23 +237,20 @@ int main(void)
if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_THUMB)) leftGamepadColor = RED;
DrawCircle(345, 260, 40, BLACK);
DrawCircle(345, 260, 35, LIGHTGRAY);
- DrawCircle(345 + (int)(leftStickX*20),
- 260 + (int)(leftStickY*20), 25, leftGamepadColor);
+ DrawCircle(345 + (int)(leftStickX*20), 260 + (int)(leftStickY*20), 25, leftGamepadColor);
// Draw axis: right joystick
Color rightGamepadColor = BLACK;
if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_THUMB)) rightGamepadColor = RED;
DrawCircle(465, 260, 40, BLACK);
DrawCircle(465, 260, 35, LIGHTGRAY);
- DrawCircle(465 + (int)(rightStickX*20),
- 260 + (int)(rightStickY*20), 25, rightGamepadColor);
+ DrawCircle(465 + (int)(rightStickX*20), 260 + (int)(rightStickY*20), 25, rightGamepadColor);
// Draw axis: left-right triggers
DrawRectangle(151, 110, 15, 70, GRAY);
DrawRectangle(644, 110, 15, 70, GRAY);
DrawRectangle(151, 110, 15, (int)(((1 + leftTrigger)/2)*70), RED);
DrawRectangle(644, 110, 15, (int)(((1 + rightTrigger)/2)*70), RED);
-
}
DrawText(TextFormat("DETECTED AXIS [%i]:", GetGamepadAxisCount(gamepad)), 10, 50, 10, MAROON);
@@ -263,13 +260,16 @@ int main(void)
DrawText(TextFormat("AXIS %i: %.02f", i, GetGamepadAxisMovement(gamepad, i)), 20, 70 + 20*i, 10, DARKGRAY);
}
+ // Draw vibrate button
+ DrawRectangleRec(vibrateButton, SKYBLUE);
+ DrawText("VIBRATE", vibrateButton.x + 14, 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);
}
else
{
DrawText(TextFormat("GP%d: NOT DETECTED", gamepad), 10, 10, 10, GRAY);
-
DrawTexture(texXboxPad, 0, 0, LIGHTGRAY);
}
diff --git a/examples/core/core_input_gestures.c b/examples/core/core_input_gestures.c
index fd7250658..e9f43ee3b 100644
--- a/examples/core/core_input_gestures.c
+++ b/examples/core/core_input_gestures.c
@@ -118,4 +118,6 @@ int main(void)
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
+
+ return 0;
}
\ No newline at end of file
diff --git a/examples/core/core_input_gestures_testbed.c b/examples/core/core_input_gestures_testbed.c
index dc47136c2..e0ffeb13f 100644
--- a/examples/core/core_input_gestures_testbed.c
+++ b/examples/core/core_input_gestures_testbed.c
@@ -22,9 +22,9 @@
#define GESTURE_LOG_SIZE 20
#define MAX_TOUCH_COUNT 32
-//----------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------
// Module Functions Declaration
-//----------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------
static char const *GetGestureName(int gesture); // Get text string for gesture value
static Color GetGestureColor(int gesture); // Get color for gesture value
@@ -69,7 +69,6 @@ int main(void)
float angleLength = 90.0f;
float currentAngleDegrees = 0.0f;
Vector2 finalVector = { 0.0f, 0.0f };
- char currentAngleStr[7] = "";
Vector2 protractorPosition = { 266.0f, 315.0f };
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
@@ -203,7 +202,7 @@ int main(void)
DrawText("Log", (int)gestureLogPosition.x, (int)gestureLogPosition.y, 20, BLACK);
// Loop in both directions to print the gesture log array in the inverted order (and looping around if the index started somewhere in the middle)
- for (i = 0, ii = gestureLogIndex; i < GESTURE_LOG_SIZE; i++, ii = (ii + 1) % GESTURE_LOG_SIZE) DrawText(gestureLog[ii], (int)gestureLogPosition.x, (int)gestureLogPosition.y + 410 - i*20, 20, (i == 0 ? gestureColor : LIGHTGRAY));
+ for (i = 0, ii = gestureLogIndex; i < GESTURE_LOG_SIZE; i++, ii = (ii + 1)%GESTURE_LOG_SIZE) DrawText(gestureLog[ii], (int)gestureLogPosition.x, (int)gestureLogPosition.y + 410 - i*20, 20, (i == 0 ? gestureColor : LIGHTGRAY));
Color logButton1Color, logButton2Color;
switch (logMode)
{
diff --git a/examples/core/core_input_keyboard_gamepad_test.c b/examples/core/core_input_keyboard_gamepad_test.c
new file mode 100644
index 000000000..d1f9106f7
--- /dev/null
+++ b/examples/core/core_input_keyboard_gamepad_test.c
@@ -0,0 +1,173 @@
+/*******************************************************************************************
+*
+* raylib [core] example - Keyboard vs Gamepad Input Test
+*
+* Example complexity rating: [★☆☆☆] 1/4
+*
+* This example is a diagnostic tool to verify that keyboard input is not
+* incorrectly detected as gamepad input on Android devices.
+*
+* Issue reference: https://github.com/raysan5/raylib/issues/5387
+*
+* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+* BSD-like license that allows static linking with closed source software
+*
+* Copyright (c) 2025 raylib contributors
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [core] example - keyboard vs gamepad test");
+
+ Vector2 ballPosition = { (float)screenWidth/2, (float)screenHeight/2 };
+ int lastKeyPressed = 0;
+
+ SetTargetFPS(60);
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose())
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+
+ // Track keyboard input
+ if (IsKeyDown(KEY_RIGHT)) ballPosition.x += 4.0f;
+ if (IsKeyDown(KEY_LEFT)) ballPosition.x -= 4.0f;
+ if (IsKeyDown(KEY_UP)) ballPosition.y -= 4.0f;
+ if (IsKeyDown(KEY_DOWN)) ballPosition.y += 4.0f;
+
+ // Keep ball on screen
+ if (ballPosition.x < 25) ballPosition.x = 25;
+ if (ballPosition.x > screenWidth - 25) ballPosition.x = screenWidth - 25;
+ if (ballPosition.y < 25) ballPosition.y = 25;
+ if (ballPosition.y > screenHeight - 25) ballPosition.y = screenHeight - 25;
+
+ // Track last key pressed
+ int key = GetKeyPressed();
+ if (key != 0) lastKeyPressed = key;
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ // Title
+ DrawText("KEYBOARD vs GAMEPAD INPUT TEST", 180, 10, 20, DARKGRAY);
+ DrawText("Issue #5387: Keyboard detected as gamepad on some Android devices", 120, 35, 14, GRAY);
+
+ // Divider
+ DrawLine(0, 60, screenWidth, 60, LIGHTGRAY);
+
+ // Keyboard section
+ DrawText("KEYBOARD INPUT", 20, 75, 18, DARKBLUE);
+ DrawRectangle(20, 100, 360, 80, Fade(BLUE, 0.1f));
+
+ DrawText(TextFormat("Arrow Keys: [%s] [%s] [%s] [%s]",
+ IsKeyDown(KEY_UP) ? "UP" : "--",
+ IsKeyDown(KEY_DOWN) ? "DN" : "--",
+ IsKeyDown(KEY_LEFT) ? "LT" : "--",
+ IsKeyDown(KEY_RIGHT) ? "RT" : "--"), 30, 110, 16, BLACK);
+
+ DrawText(TextFormat("Last Key Pressed: %d", lastKeyPressed), 30, 135, 16, DARKGRAY);
+ DrawText(TextFormat("Any Key Down: %s", (IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) ||
+ IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT)) ? "YES" : "NO"), 30, 155, 16, DARKGRAY);
+
+ // Gamepad section
+ DrawText("GAMEPAD STATUS", 420, 75, 18, DARKGREEN);
+ DrawRectangle(420, 100, 360, 80, Fade(GREEN, 0.1f));
+
+ bool gamepadReady = IsGamepadAvailable(0);
+ DrawText(TextFormat("Gamepad 0 Available: %s", gamepadReady ? "YES" : "NO"),
+ 430, 110, 16, gamepadReady ? RED : DARKGREEN);
+
+ if (gamepadReady)
+ {
+ DrawText(TextFormat("D-Pad: [%s] [%s] [%s] [%s]",
+ IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_UP) ? "UP" : "--",
+ IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_DOWN) ? "DN" : "--",
+ IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_LEFT) ? "LT" : "--",
+ IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_RIGHT) ? "RT" : "--"),
+ 430, 135, 16, RED);
+
+ DrawText(TextFormat("Gamepad Name: %.20s", GetGamepadName(0)), 430, 155, 14, DARKGRAY);
+ }
+ else
+ {
+ DrawText("No gamepad detected", 430, 135, 16, DARKGREEN);
+ }
+
+ // Divider
+ DrawLine(0, 190, screenWidth, 190, LIGHTGRAY);
+
+ // Test result section
+ DrawText("TEST RESULT", 20, 200, 18, MAROON);
+
+ bool keyboardActive = IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) ||
+ IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT);
+
+ if (keyboardActive && gamepadReady)
+ {
+ // BUG DETECTED: Keyboard is triggering gamepad detection
+ DrawRectangle(20, 225, 760, 50, Fade(RED, 0.3f));
+ DrawText("BUG DETECTED: Keyboard input is being detected as gamepad!", 30, 235, 18, RED);
+ DrawText("The fix for issue #5387 may not be working correctly.", 30, 258, 14, DARKGRAY);
+ }
+ else if (keyboardActive && !gamepadReady)
+ {
+ // CORRECT: Keyboard works without triggering gamepad
+ DrawRectangle(20, 225, 760, 50, Fade(GREEN, 0.3f));
+ DrawText("PASS: Keyboard input detected correctly (no phantom gamepad)", 30, 235, 18, DARKGREEN);
+ DrawText("Issue #5387 fix is working as expected.", 30, 258, 14, DARKGRAY);
+ }
+ else if (!keyboardActive && gamepadReady)
+ {
+ // Gamepad is connected (might be real or might be bug on idle)
+ DrawRectangle(20, 225, 760, 50, Fade(ORANGE, 0.3f));
+ DrawText("INFO: Gamepad detected - press keyboard keys to test", 30, 235, 18, ORANGE);
+ DrawText("If gamepad stays active while pressing keyboard = BUG", 30, 258, 14, DARKGRAY);
+ }
+ else
+ {
+ // Idle state
+ DrawRectangle(20, 225, 760, 50, Fade(GRAY, 0.1f));
+ DrawText("WAITING: Press arrow keys to test keyboard input", 30, 235, 18, GRAY);
+ DrawText("Gamepad should NOT become available when pressing keyboard keys", 30, 258, 14, DARKGRAY);
+ }
+
+ // Ball controlled by keyboard
+ DrawText("Ball Control (Arrow Keys):", 20, 295, 16, DARKGRAY);
+ DrawCircleV(ballPosition, 25, MAROON);
+ DrawCircleLines((int)ballPosition.x, (int)ballPosition.y, 25, DARKGRAY);
+
+ // Instructions
+ DrawRectangle(0, screenHeight - 45, screenWidth, 45, Fade(BLACK, 0.05f));
+ DrawText("Instructions: Press keyboard arrow keys - the ball should move and gamepad should stay 'NO'",
+ 20, screenHeight - 35, 14, DARKGRAY);
+ DrawText("If gamepad becomes 'YES' while pressing keyboard = issue #5387 is NOT fixed",
+ 20, screenHeight - 18, 14, DARKGRAY);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ CloseWindow();
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
diff --git a/examples/core/core_input_mouse.c b/examples/core/core_input_mouse.c
index f429018b0..d46106996 100644
--- a/examples/core/core_input_mouse.c
+++ b/examples/core/core_input_mouse.c
@@ -40,14 +40,8 @@ int main(void)
//----------------------------------------------------------------------------------
if (IsKeyPressed(KEY_H))
{
- if (IsCursorHidden())
- {
- ShowCursor();
- }
- else
- {
- HideCursor();
- }
+ if (IsCursorHidden()) ShowCursor();
+ else HideCursor();
}
ballPosition = GetMousePosition();
diff --git a/examples/core/core_input_multitouch.c b/examples/core/core_input_multitouch.c
index 01dd90fae..47ad91d66 100644
--- a/examples/core/core_input_multitouch.c
+++ b/examples/core/core_input_multitouch.c
@@ -46,7 +46,7 @@ int main(void)
// Clamp touch points available ( set the maximum touch points allowed )
if (tCount > MAX_TOUCH_POINTS) tCount = MAX_TOUCH_POINTS;
// Get touch points positions
- for (int i = 0; i < tCount; ++i) touchPositions[i] = GetTouchPosition(i);
+ for (int i = 0; i < tCount; i++) touchPositions[i] = GetTouchPosition(i);
//----------------------------------------------------------------------------------
// Draw
@@ -55,7 +55,7 @@ int main(void)
ClearBackground(RAYWHITE);
- for (int i = 0; i < tCount; ++i)
+ for (int i = 0; i < tCount; i++)
{
// Make sure point is not (0, 0) as this means there is no touch for it
if ((touchPositions[i].x > 0) && (touchPositions[i].y > 0))
diff --git a/examples/core/core_input_virtual_controls.c b/examples/core/core_input_virtual_controls.c
index 6cae4b6c7..db293b993 100644
--- a/examples/core/core_input_virtual_controls.c
+++ b/examples/core/core_input_virtual_controls.c
@@ -6,7 +6,7 @@
*
* Example originally created with raylib 5.0, last time updated with raylib 5.0
*
-* Example contributed by GreenSnakeLinux (@GreenSnakeLinux),
+* Example contributed by GreenSnakeLinux (@GreenSnakeLinux),
* reviewed by Ramon Santamaria (@raysan5), oblerion (@oblerion) and danilwhale (@danilwhale)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
@@ -86,7 +86,7 @@ int main(void)
pressedButton = BUTTON_NONE;
// Make sure user is pressing left mouse button if they're from desktop
- if ((GetTouchPointCount() > 0) ||
+ if ((GetTouchPointCount() > 0) ||
((GetTouchPointCount() == 0) && IsMouseButtonDown(MOUSE_BUTTON_LEFT)))
{
// Find nearest D-Pad button to the input position
@@ -113,7 +113,7 @@ int main(void)
default: break;
};
//--------------------------------------------------------------------------
-
+
// Draw
//--------------------------------------------------------------------------
BeginDrawing();
diff --git a/examples/core/core_monitor_detector.c b/examples/core/core_monitor_detector.c
index 0e94f6895..ab65d8042 100644
--- a/examples/core/core_monitor_detector.c
+++ b/examples/core/core_monitor_detector.c
@@ -40,10 +40,9 @@ int main(void)
const int screenWidth = 800;
const int screenHeight = 450;
- MonitorInfo monitors[MAX_MONITORS] = { 0 };
-
InitWindow(screenWidth, screenHeight, "raylib [core] example - monitor detector");
+ MonitorInfo monitors[MAX_MONITORS] = { 0 };
int currentMonitorIndex = GetCurrentMonitor();
int monitorCount = 0;
@@ -55,7 +54,6 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
-
// Variables to find the max x and Y to calculate the scale
int maxWidth = 1;
int maxHeight = 1;
@@ -68,42 +66,40 @@ int main(void)
for (int i = 0; i < monitorCount; i++)
{
monitors[i] = (MonitorInfo){
- GetMonitorPosition(i),
- GetMonitorName(i),
+ GetMonitorPosition(i),
+ GetMonitorName(i),
GetMonitorWidth(i),
GetMonitorHeight(i),
GetMonitorPhysicalWidth(i),
GetMonitorPhysicalHeight(i),
GetMonitorRefreshRate(i)
};
- if (monitors[i].position.x < monitorOffsetX) monitorOffsetX = (int)monitors[i].position.x*-1;
+
+ if (monitors[i].position.x < monitorOffsetX) monitorOffsetX = -(int)monitors[i].position.x;
const int width = (int)monitors[i].position.x + monitors[i].width;
const int height = (int)monitors[i].position.y + monitors[i].height;
-
+
if (maxWidth < width) maxWidth = width;
if (maxHeight < height) maxHeight = height;
}
- if (IsKeyPressed(KEY_ENTER) && monitorCount > 1)
+ if (IsKeyPressed(KEY_ENTER) && (monitorCount > 1))
{
currentMonitorIndex += 1;
// Set index to 0 if the last one
- if(currentMonitorIndex == monitorCount) currentMonitorIndex = 0;
+ if (currentMonitorIndex == monitorCount) currentMonitorIndex = 0;
SetWindowMonitor(currentMonitorIndex); // Move window to currentMonitorIndex
}
- else
- {
- // Get currentMonitorIndex if manually moved
- currentMonitorIndex = GetCurrentMonitor();
- }
+ else currentMonitorIndex = GetCurrentMonitor(); // Get currentMonitorIndex if manually moved
- float monitorScale = 0.6f;
+ float monitorScale = 0.6f;
- if(maxHeight > maxWidth + monitorOffsetX) monitorScale *= ((float)screenHeight/(float)maxHeight);
+ if (maxHeight > (maxWidth + monitorOffsetX)) monitorScale *= ((float)screenHeight/(float)maxHeight);
else monitorScale *= ((float)screenWidth/(float)(maxWidth + monitorOffsetX));
+ //----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
@@ -120,18 +116,18 @@ int main(void)
{
// Calculate retangle position and size using monitorScale
const Rectangle rec = (Rectangle){
- (monitors[i].position.x + monitorOffsetX) * monitorScale + 140,
- monitors[i].position.y * monitorScale + 80,
- monitors[i].width * monitorScale,
- monitors[i].height * monitorScale
+ (monitors[i].position.x + monitorOffsetX)*monitorScale + 140,
+ monitors[i].position.y*monitorScale + 80,
+ monitors[i].width*monitorScale,
+ monitors[i].height*monitorScale
};
// Draw monitor name and information inside the rectangle
DrawText(TextFormat("[%i] %s", i, monitors[i].name), (int)rec.x + 10, (int)rec.y + (int)(100*monitorScale), (int)(120*monitorScale), BLUE);
DrawText(
- TextFormat("Resolution: [%ipx x %ipx]\nRefreshRate: [%ihz]\nPhysical Size: [%imm x %imm]\nPosition: %3.0f x %3.0f",
- monitors[i].width,
- monitors[i].height,
+ TextFormat("Resolution: [%ipx x %ipx]\nRefreshRate: [%ihz]\nPhysical Size: [%imm x %imm]\nPosition: %3.0f x %3.0f",
+ monitors[i].width,
+ monitors[i].height,
monitors[i].refreshRate,
monitors[i].physicalWidth,
monitors[i].physicalHeight,
@@ -146,16 +142,11 @@ int main(void)
Vector2 windowPosition = (Vector2){ (GetWindowPosition().x + monitorOffsetX)*monitorScale + 140, GetWindowPosition().y*monitorScale + 80 };
// Draw window position based on monitors
- DrawRectangleV(windowPosition, (Vector2){screenWidth * monitorScale, screenHeight * monitorScale}, Fade(GREEN, 0.5));
+ DrawRectangleV(windowPosition, (Vector2){screenWidth*monitorScale, screenHeight*monitorScale}, Fade(GREEN, 0.5));
}
- else
- {
- DrawRectangleLinesEx(rec, 5, GRAY);
- }
-
+ else DrawRectangleLinesEx(rec, 5, GRAY);
}
-
EndDrawing();
//----------------------------------------------------------------------------------
}
diff --git a/examples/core/core_screen_recording.c b/examples/core/core_screen_recording.c
index 43c90eec5..633ddcf81 100644
--- a/examples/core/core_screen_recording.c
+++ b/examples/core/core_screen_recording.c
@@ -2,12 +2,10 @@
*
* raylib [core] example - screen recording
*
-* Example complexity rating: [★☆☆☆] 1/4
+* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
*
-* Example contributed by Ramon Santamaria (@raysan5) and reviewed by Ramon Santamaria (@raysan5)
-*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
@@ -17,6 +15,16 @@
#include "raylib.h"
+// Using msf_gif library to record frames into GIF
+#define MSF_GIF_IMPL
+#include "msf_gif.h" // GIF recording functionality
+
+#include // Required for: sinf()
+
+#define GIF_RECORD_FRAMERATE 5 // Record framerate, we get a frame every N frames
+
+#define MAX_SINEWAVE_POINTS 256
+
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
@@ -29,7 +37,20 @@ int main(void)
InitWindow(screenWidth, screenHeight, "raylib [core] example - screen recording");
- // TODO: Load resources / Initialize variables at this point
+ bool gifRecording = false; // GIF recording state
+ unsigned int gifFrameCounter = 0; // GIF frames counter
+ MsfGifState gifState = { 0 }; // MSGIF context state
+
+ Vector2 circlePosition = { 0.0f, screenHeight/2.0f };
+ float timeCounter = 0.0f;
+
+ // Get sine wave points for line drawing
+ Vector2 sinePoints[MAX_SINEWAVE_POINTS] = { 0 };
+ for (int i = 0; i < MAX_SINEWAVE_POINTS; i++)
+ {
+ sinePoints[i].x = i*GetScreenWidth()/180.0f;
+ sinePoints[i].y = screenHeight/2.0f + 150*sinf((2*PI/1.5f)*(1.0f/60.0f)*(float)i); // Calculate for 60 fps
+ }
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
@@ -39,7 +60,59 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
- // TODO: Update variables / Implement example logic at this point
+ // Update circle sinusoidal movement
+ timeCounter += GetFrameTime();
+ circlePosition.x += GetScreenWidth()/180.0f;
+ circlePosition.y = screenHeight/2.0f + 150*sinf((2*PI/1.5f)*timeCounter);
+ if (circlePosition.x > screenWidth)
+ {
+ circlePosition.x = 0.0f;
+ circlePosition.y = screenHeight/2.0f;
+ timeCounter = 0.0f;
+ }
+
+ // Start-Stop GIF recording on CTRL+R
+ if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_R))
+ {
+ if (gifRecording)
+ {
+ // Stop current recording and save file
+ gifRecording = false;
+ MsfGifResult result = msf_gif_end(&gifState);
+ SaveFileData(TextFormat("%s/screenrecording.gif", GetApplicationDirectory()), result.data, (unsigned int)result.dataSize);
+ msf_gif_free(result);
+
+ TraceLog(LOG_INFO, "Finish animated GIF recording");
+ }
+ else
+ {
+ // Start a new recording
+ gifRecording = true;
+ gifFrameCounter = 0;
+ msf_gif_begin(&gifState, GetRenderWidth(), GetRenderHeight());
+
+ TraceLog(LOG_INFO, "Start animated GIF recording");
+ }
+ }
+
+ if (gifRecording)
+ {
+ gifFrameCounter++;
+
+ // NOTE: We record one gif frame depending on the desired gif framerate
+ if (gifFrameCounter > GIF_RECORD_FRAMERATE)
+ {
+ // Get image data for the current frame (from backbuffer)
+ // WARNING: This process is quite slow, it can generate stuttering
+ Image imScreen = LoadImageFromScreen();
+
+ // Add the frame to the gif recording, providing and "estimated" time for display in centiseconds
+ msf_gif_frame(&gifState, imScreen.data, (int)((1.0f/60.0f)*GIF_RECORD_FRAMERATE)/10, 16, imScreen.width*4);
+ gifFrameCounter = 0;
+
+ UnloadImage(imScreen); // Free image data
+ }
+ }
//----------------------------------------------------------------------------------
// Draw
@@ -48,20 +121,43 @@ int main(void)
ClearBackground(RAYWHITE);
- // TODO: Draw everything that requires to be drawn at this point
+ for (int i = 0; i < (MAX_SINEWAVE_POINTS - 1); i++)
+ {
+ DrawLineV(sinePoints[i], sinePoints[i + 1], MAROON);
+ DrawCircleV(sinePoints[i], 3, MAROON);
+ }
- DrawLineEx((Vector2){ 0, 0 }, (Vector2){ screenWidth, screenHeight }, 2.0f, RED);
- DrawLineEx((Vector2){ 0, screenHeight }, (Vector2){ screenWidth, 0 }, 2.0f, RED);
- DrawText("example base code template", 260, 400, 20, LIGHTGRAY);
+ DrawCircleV(circlePosition, 30, RED);
+ DrawFPS(10, 10);
+
+ /*
+ // Draw record indicator
+ // WARNING: If drawn here, it will appear in the recorded image,
+ // use a render texture instead for the recording and LoadImageFromTexture(rt.texture)
+ if (gifRecording)
+ {
+ // Display the recording indicator every half-second
+ if ((int)(GetTime()/0.5)%2 == 1)
+ {
+ DrawCircle(30, GetScreenHeight() - 20, 10, MAROON);
+ DrawText("GIF RECORDING", 50, GetScreenHeight() - 25, 10, RED);
+ }
+ }
+ */
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
-
- // TODO: Unload all loaded resources at this point
+ // If still recording a GIF on close window, just finish
+ if (gifRecording)
+ {
+ MsfGifResult result = msf_gif_end(&gifState);
+ msf_gif_free(result);
+ gifRecording = false;
+ }
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
diff --git a/examples/core/core_screen_recording.png b/examples/core/core_screen_recording.png
index da99bbb0d..9b14af24d 100644
Binary files a/examples/core/core_screen_recording.png and b/examples/core/core_screen_recording.png differ
diff --git a/examples/core/core_text_file_loading.c b/examples/core/core_text_file_loading.c
index ed48f7d36..852cc32c4 100644
--- a/examples/core/core_text_file_loading.c
+++ b/examples/core/core_text_file_loading.c
@@ -19,6 +19,8 @@
#include "raymath.h" // Required for: Lerp()
+#include
+
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
@@ -59,10 +61,11 @@ int main(void)
int lastSpace = 0; // Keeping track of last valid space to insert '\n'
int lastWrapStart = 0; // Keeping track of the start of this wrapped line.
- while (lines[i][j] != '\0')
+ while (j <= strlen(lines[i]))
{
- if (lines[i][j] == ' ')
+ if (lines[i][j] == ' ' || lines[i][j] == '\0')
{
+ char before = lines[i][j];
// Making a C Style string by adding a '\0' at the required location so that we can use the MeasureText function
lines[i][j] = '\0';
@@ -75,7 +78,7 @@ int main(void)
lastWrapStart = lastSpace + 1;
}
- lines[i][j] = ' '; // Resetting the space back
+ if(before != '\0') lines[i][j] = ' '; // Resetting the space back
lastSpace = j; // Since we encountered a new space we update our last encountered space location
}
@@ -92,7 +95,7 @@ int main(void)
textHeight += (int)size.y + 10;
}
- // A simple scrollbar on the side to show how far we have red into the file
+ // A simple scrollbar on the side to show how far we have read into the file
Rectangle scrollBar = {
.x = (float)screenWidth - 5,
.y = 0,
@@ -132,7 +135,13 @@ int main(void)
for (int i = 0, t = textTop; i < lineCount; i++)
{
// Each time we go through and calculate the height of the text to move the cursor appropriately
- Vector2 size = MeasureTextEx(GetFontDefault(), lines[i], (float)fontSize, 2);
+ Vector2 size;
+ if(strcmp(lines[i], "")){
+ // Fix for empty line in the text file
+ size = MeasureTextEx( GetFontDefault(), lines[i], (float)fontSize, 2);
+ }else{
+ size = MeasureTextEx( GetFontDefault(), " ", (float)fontSize, 2);
+ }
DrawText(lines[i], 10, t, fontSize, RED);
diff --git a/examples/core/core_undo_redo.c b/examples/core/core_undo_redo.c
index 45b19e10f..78971e689 100644
--- a/examples/core/core_undo_redo.c
+++ b/examples/core/core_undo_redo.c
@@ -22,7 +22,7 @@
#define MAX_UNDO_STATES 26 // Maximum undo states supported for the ring buffer
-#define GRID_CELL_SIZE 24
+#define GRID_CELL_SIZE 24
#define MAX_GRID_CELLS_X 30
#define MAX_GRID_CELLS_Y 13
@@ -57,7 +57,7 @@ int main(void)
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
-
+
// We have multiple options to implement an Undo/Redo system
// Probably the most professional one is using the Command pattern to
// define Actions and store those actions into an array as the events happen,
@@ -187,7 +187,7 @@ int main(void)
if (lastUndoIndex > firstUndoIndex)
{
for (int i = firstUndoIndex; i < currentUndoIndex; i++)
- DrawRectangleRec((Rectangle){gridPosition.x + states[i].cell.x * GRID_CELL_SIZE, gridPosition.y + states[i].cell.y * GRID_CELL_SIZE,
+ DrawRectangleRec((Rectangle){gridPosition.x + states[i].cell.x*GRID_CELL_SIZE, gridPosition.y + states[i].cell.y*GRID_CELL_SIZE,
GRID_CELL_SIZE, GRID_CELL_SIZE }, LIGHTGRAY);
}
else if (firstUndoIndex > lastUndoIndex)
@@ -195,7 +195,7 @@ int main(void)
if ((currentUndoIndex < MAX_UNDO_STATES) && (currentUndoIndex > lastUndoIndex))
{
for (int i = firstUndoIndex; i < currentUndoIndex; i++)
- DrawRectangleRec((Rectangle) { gridPosition.x + states[i].cell.x * GRID_CELL_SIZE, gridPosition.y + states[i].cell.y * GRID_CELL_SIZE,
+ DrawRectangleRec((Rectangle) { gridPosition.x + states[i].cell.x*GRID_CELL_SIZE, gridPosition.y + states[i].cell.y*GRID_CELL_SIZE,
GRID_CELL_SIZE, GRID_CELL_SIZE }, LIGHTGRAY);
}
else
diff --git a/examples/core/core_viewport_scaling.c b/examples/core/core_viewport_scaling.c
new file mode 100644
index 000000000..adcd51ea3
--- /dev/null
+++ b/examples/core/core_viewport_scaling.c
@@ -0,0 +1,321 @@
+/*******************************************************************************************
+*
+* raylib [core] example - viewport scaling
+*
+* Example complexity rating: [★★☆☆] 2/4
+*
+* Example originally created with raylib 5.5, last time updated with raylib 5.5
+*
+* Example contributed by Agnis Aldiņš (@nezvers) 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 Agnis Aldiņš (@nezvers)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define RESOLUTION_COUNT 4 // For iteration purposes and teaching example
+
+typedef enum {
+ // Only upscale, useful for pixel art
+ KEEP_ASPECT_INTEGER,
+ KEEP_HEIGHT_INTEGER,
+ KEEP_WIDTH_INTEGER,
+ // Can also downscale
+ KEEP_ASPECT,
+ KEEP_HEIGHT,
+ KEEP_WIDTH,
+ // For itteration purposes and as a teaching example
+ VIEWPORT_TYPE_COUNT,
+} ViewportType;
+
+// For displaying on GUI
+const char *ViewportTypeNames[VIEWPORT_TYPE_COUNT] = {
+ "KEEP_ASPECT_INTEGER",
+ "KEEP_HEIGHT_INTEGER",
+ "KEEP_WIDTH_INTEGER",
+ "KEEP_ASPECT",
+ "KEEP_HEIGHT",
+ "KEEP_WIDTH",
+};
+
+//--------------------------------------------------------------------------------------
+// Module Functions Declaration
+//--------------------------------------------------------------------------------------
+static void KeepAspectCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
+static void KeepHeightCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
+static void KeepWidthCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
+static void KeepAspectCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
+static void KeepHeightCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
+static void KeepWidthCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
+static void ResizeRenderSize(ViewportType viewportType, int *screenWidth, int *screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect, RenderTexture2D *target);
+
+// Example how to calculate position on RenderTexture
+static Vector2 Screen2RenderTexturePosition(Vector2 point, Rectangle *textureRect, Rectangle *scaledRect);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //---------------------------------------------------------
+ int screenWidth = 800;
+ int screenHeight = 450;
+
+ SetConfigFlags(FLAG_WINDOW_RESIZABLE);
+ InitWindow(screenWidth, screenHeight, "raylib [core] example - viewport scaling");
+
+ // Preset resolutions that could be created by subdividing screen resolution
+ Vector2 resolutionList[RESOLUTION_COUNT] = {
+ (Vector2){ 64, 64 },
+ (Vector2){ 256, 240 },
+ (Vector2){ 320, 180 },
+ // 4K doesn't work with integer scaling but included for example purposes with non-integer scaling
+ (Vector2){ 3840, 2160 },
+ };
+
+ int resolutionIndex = 0;
+ int gameWidth = 64;
+ int gameHeight = 64;
+
+ RenderTexture2D target = (RenderTexture2D){ 0 };
+ Rectangle sourceRect = (Rectangle){ 0 };
+ Rectangle destRect = (Rectangle){ 0 };
+
+ ViewportType viewportType = KEEP_ASPECT_INTEGER;
+ ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
+
+ // Button rectangles
+ Rectangle decreaseResolutionButton = (Rectangle){ 200, 30, 10, 10 };
+ Rectangle increaseResolutionButton = (Rectangle){ 215, 30, 10, 10 };
+ Rectangle decreaseTypeButton = (Rectangle){ 200, 45, 10, 10 };
+ Rectangle increaseTypeButton = (Rectangle){ 215, 45, 10, 10 };
+
+ 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
+ //----------------------------------------------------------------------------------
+ if (IsWindowResized()) ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
+
+ Vector2 mousePosition = GetMousePosition();
+ bool mousePressed = IsMouseButtonPressed(MOUSE_BUTTON_LEFT);
+
+ // Check buttons and rescale
+ if (CheckCollisionPointRec(mousePosition, decreaseResolutionButton) && mousePressed)
+ {
+ resolutionIndex = (resolutionIndex + RESOLUTION_COUNT - 1)%RESOLUTION_COUNT;
+ gameWidth = resolutionList[resolutionIndex].x;
+ gameHeight = 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;
+ ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
+ }
+
+ if (CheckCollisionPointRec(mousePosition, decreaseTypeButton) && mousePressed)
+ {
+ viewportType = (viewportType + VIEWPORT_TYPE_COUNT - 1)%VIEWPORT_TYPE_COUNT;
+ ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
+ }
+
+ if (CheckCollisionPointRec(mousePosition, increaseTypeButton) && mousePressed)
+ {
+ viewportType = (viewportType + 1)%VIEWPORT_TYPE_COUNT;
+ ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
+ }
+
+ Vector2 textureMousePosition = Screen2RenderTexturePosition(mousePosition, &sourceRect, &destRect);
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ // Draw our scene to the render texture
+ BeginTextureMode(target);
+ ClearBackground(WHITE);
+ DrawCircle(textureMousePosition.x, textureMousePosition.y, 20.0f, LIME);
+ EndTextureMode();
+
+ // Draw render texture to main framebuffer
+ BeginDrawing();
+ ClearBackground(BLACK);
+
+ // Draw our render texture with rotation applied
+ DrawTexturePro(target.texture, sourceRect, destRect, (Vector2){ 0.0f, 0.0f }, 0.0f, WHITE);
+
+ // Draw Native resolution (GUI or anything)
+ // 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);
+
+ 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);
+
+ DrawText(TextFormat("Type: %s", ViewportTypeNames[viewportType]), 15, 45, 10, BLACK);
+ Vector2 scaleRatio = (Vector2){destRect.width/sourceRect.width, -destRect.height/sourceRect.height};
+ if (scaleRatio.x < 0.001f || scaleRatio.y < 0.001f) DrawText(TextFormat("Scale ratio: INVALID"), 15, 60, 10, BLACK);
+ else DrawText(TextFormat("Scale ratio: %.2f x %.2f", scaleRatio.x, scaleRatio.y), 15, 60, 10, BLACK);
+
+ DrawText(TextFormat("Source size: %.2f x %.2f", sourceRect.width, -sourceRect.height), 15, 75, 10, BLACK);
+ DrawText(TextFormat("Destination size: %.2f x %.2f", destRect.width, destRect.height), 15, 90, 10, BLACK);
+
+ // Draw buttons
+ DrawRectangleRec(decreaseTypeButton, SKYBLUE);
+ DrawRectangleRec(increaseTypeButton, SKYBLUE);
+ DrawRectangleRec(decreaseResolutionButton, SKYBLUE);
+ DrawRectangleRec(increaseResolutionButton, SKYBLUE);
+ DrawText("<", decreaseTypeButton.x + 3, decreaseTypeButton.y + 1, 10, BLACK);
+ DrawText(">", increaseTypeButton.x + 3, increaseTypeButton.y + 1, 10, BLACK);
+ DrawText("<", decreaseResolutionButton.x + 3, decreaseResolutionButton.y + 1, 10, BLACK);
+ DrawText(">", increaseResolutionButton.x + 3, increaseResolutionButton.y + 1, 10, BLACK);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //----------------------------------------------------------------------------------
+ CloseWindow(); // Close window and OpenGL context
+ //----------------------------------------------------------------------------------
+
+ return 0;
+}
+
+//--------------------------------------------------------------------------------------
+// Module Functions Definition
+//--------------------------------------------------------------------------------------
+static void KeepAspectCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
+{
+ sourceRect->x = 0.0f;
+ sourceRect->y = (float)gameHeight;
+ sourceRect->width = (float)gameWidth;
+ sourceRect->height = (float)-gameHeight;
+
+ const int ratio_x = (screenWidth/gameWidth);
+ const int ratio_y = (screenHeight/gameHeight);
+ const float resizeRatio = (float)((ratio_x < ratio_y)? ratio_x : ratio_y);
+
+ destRect->x = (float)(int)((screenWidth - (gameWidth*resizeRatio))*0.5f);
+ destRect->y = (float)(int)((screenHeight - (gameHeight*resizeRatio))*0.5f);
+ destRect->width = (float)(int)(gameWidth*resizeRatio);
+ destRect->height = (float)(int)(gameHeight*resizeRatio);
+}
+
+static void KeepHeightCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
+{
+ const float resizeRatio = (float)(screenHeight/gameHeight);
+ sourceRect->x = 0.0f;
+ sourceRect->y = 0.0f;
+ sourceRect->width = (float)(int)(screenWidth/resizeRatio);
+ sourceRect->height = (float)-gameHeight;
+
+ destRect->x = (float)(int)((screenWidth - (sourceRect->width*resizeRatio))*0.5f);
+ destRect->y = (float)(int)((screenHeight - (gameHeight*resizeRatio))*0.5f);
+ destRect->width = (float)(int)(sourceRect->width*resizeRatio);
+ destRect->height = (float)(int)(gameHeight*resizeRatio);
+}
+
+static void KeepWidthCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
+{
+ const float resizeRatio = (float)(screenWidth/gameWidth);
+ sourceRect->x = 0.0f;
+ sourceRect->y = 0.0f;
+ sourceRect->width = (float)gameWidth;
+ sourceRect->height = (float)(int)(screenHeight/resizeRatio);
+
+ destRect->x = (float)(int)((screenWidth - (gameWidth*resizeRatio))*0.5f);
+ destRect->y = (float)(int)((screenHeight - (sourceRect->height*resizeRatio))*0.5f);
+ destRect->width = (float)(int)(gameWidth*resizeRatio);
+ destRect->height = (float)(int)(sourceRect->height*resizeRatio);
+
+ sourceRect->height *= -1.0f;
+}
+
+static void KeepAspectCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
+{
+ sourceRect->x = 0.0f;
+ sourceRect->y = (float)gameHeight;
+ sourceRect->width = (float)gameWidth;
+ sourceRect->height = (float)-gameHeight;
+
+ const float ratio_x = ((float)screenWidth/(float)gameWidth);
+ const float ratio_y = ((float)screenHeight/(float)gameHeight);
+ const float resizeRatio = (ratio_x < ratio_y ? ratio_x : ratio_y);
+
+ destRect->x = (float)(int)((screenWidth - (gameWidth*resizeRatio))*0.5f);
+ destRect->y = (float)(int)((screenHeight - (gameHeight*resizeRatio))*0.5f);
+ destRect->width = (float)(int)(gameWidth*resizeRatio);
+ destRect->height = (float)(int)(gameHeight*resizeRatio);
+}
+
+static void KeepHeightCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
+{
+ const float resizeRatio = ((float)screenHeight/(float)gameHeight);
+ sourceRect->x = 0.0f;
+ sourceRect->y = 0.0f;
+ sourceRect->width = (float)(int)((float)screenWidth/resizeRatio);
+ sourceRect->height = (float)-gameHeight;
+
+ destRect->x = (float)(int)((screenWidth - (sourceRect->width*resizeRatio))*0.5f);
+ destRect->y = (float)(int)((screenHeight - (gameHeight*resizeRatio))*0.5f);
+ destRect->width = (float)(int)(sourceRect->width*resizeRatio);
+ destRect->height = (float)(int)(gameHeight*resizeRatio);
+}
+
+static void KeepWidthCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
+{
+ const float resizeRatio = ((float)screenWidth/(float)gameWidth);
+ sourceRect->x = 0.0f;
+ sourceRect->y = 0.0f;
+ sourceRect->width = (float)gameWidth;
+ sourceRect->height = (float)(int)((float)screenHeight/resizeRatio);
+
+ destRect->x = (float)(int)((screenWidth - (gameWidth*resizeRatio))*0.5f);
+ destRect->y = (float)(int)((screenHeight - (sourceRect->height*resizeRatio))*0.5f);
+ destRect->width = (float)(int)(gameWidth*resizeRatio);
+ destRect->height = (float)(int)(sourceRect->height*resizeRatio);
+
+ sourceRect->height *= -1.f;
+}
+
+static void ResizeRenderSize(ViewportType viewportType, int *screenWidth, int *screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect, RenderTexture2D *target)
+{
+ *screenWidth = GetScreenWidth();
+ *screenHeight = GetScreenHeight();
+
+ switch(viewportType)
+ {
+ case KEEP_ASPECT_INTEGER: KeepAspectCenteredInteger(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
+ case KEEP_HEIGHT_INTEGER: KeepHeightCenteredInteger(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
+ case KEEP_WIDTH_INTEGER: KeepWidthCenteredInteger(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
+ case KEEP_ASPECT: KeepAspectCentered(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
+ case KEEP_HEIGHT: KeepHeightCentered(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
+ case KEEP_WIDTH: KeepWidthCentered(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
+ default: break;
+ }
+
+ UnloadRenderTexture(*target);
+ *target = LoadRenderTexture(sourceRect->width, -sourceRect->height);
+}
+
+// Example how to calculate position on RenderTexture
+static Vector2 Screen2RenderTexturePosition(Vector2 point, Rectangle *textureRect, Rectangle *scaledRect)
+{
+ Vector2 relativePosition = {point.x - scaledRect->x, point.y - scaledRect->y};
+ Vector2 ratio = {textureRect->width/scaledRect->width, -textureRect->height/scaledRect->height};
+
+ return (Vector2){relativePosition.x*ratio.x, relativePosition.y*ratio.x};
+}
\ No newline at end of file
diff --git a/examples/core/core_viewport_scaling.png b/examples/core/core_viewport_scaling.png
new file mode 100644
index 000000000..68fad6209
Binary files /dev/null and b/examples/core/core_viewport_scaling.png differ
diff --git a/examples/core/core_window_flags.c b/examples/core/core_window_flags.c
index 048d2d245..185e7f22c 100644
--- a/examples/core/core_window_flags.c
+++ b/examples/core/core_window_flags.c
@@ -43,7 +43,7 @@ int main(void)
*/
// Set configuration flags for window creation
- //SetConfigFlags(FLAG_VSYNC_HINT | FLAG_MSAA_4X_HINT | FLAG_WINDOW_HIGHDPI);
+ //SetConfigFlags(FLAG_VSYNC_HINT | FLAG_MSAA_4X_HINT | FLAG_WINDOW_HIGHDPI);// | FLAG_WINDOW_TRANSPARENT);
InitWindow(screenWidth, screenHeight, "raylib [core] example - window flags");
Vector2 ballPosition = { GetScreenWidth()/2.0f, GetScreenHeight()/2.0f };
@@ -97,7 +97,8 @@ int main(void)
if (IsWindowState(FLAG_WINDOW_MINIMIZED))
{
framesCounter++;
- if (framesCounter >= 240) {
+ if (framesCounter >= 240)
+ {
RestoreWindow(); // Restore window after 3 seconds
framesCounter = 0;
}
diff --git a/examples/core/core_window_should_close.c b/examples/core/core_window_should_close.c
index bb7daf3ee..f53f9d48d 100644
--- a/examples/core/core_window_should_close.c
+++ b/examples/core/core_window_should_close.c
@@ -18,7 +18,7 @@
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
-int main()
+int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
diff --git a/src/external/msf_gif.h b/examples/core/msf_gif.h
similarity index 100%
rename from src/external/msf_gif.h
rename to examples/core/msf_gif.h
diff --git a/examples/core/raygui.h b/examples/core/raygui.h
index a3fc51f0f..88fe5cc5b 100644
--- a/examples/core/raygui.h
+++ b/examples/core/raygui.h
@@ -4,7 +4,7 @@
*
* DESCRIPTION:
* raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also
-* available as a standalone library, as long as input and drawing functions are provided.
+* available as a standalone library, as long as input and drawing functions are provided
*
* FEATURES:
* - Immediate-mode gui, minimal retained data
@@ -27,7 +27,7 @@
* - WARNING: GuiLoadStyle() and GuiLoadStyle{Custom}() functions, allocate memory for
* font atlas recs and glyphs, freeing that memory is (usually) up to the user,
* no unload function is explicitly provided... but note that GuiLoadStyleDefault() unloads
-* by default any previously loaded font (texture, recs, glyphs).
+* by default any previously loaded font (texture, recs, glyphs)
* - Global UI alpha (guiAlpha) is applied inside GuiDrawRectangle() and GuiDrawText() functions
*
* CONTROLS PROVIDED:
@@ -65,7 +65,7 @@
* - MessageBox --> Window, Label, Button
* - TextInputBox --> Window, Label, TextBox, Button
*
-* It also provides a set of functions for styling the controls based on its properties (size, color).
+* It also provides a set of functions for styling the controls based on its properties (size, color)
*
*
* RAYGUI STYLE (guiStyle):
@@ -77,11 +77,11 @@
*
* static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)];
*
-* guiStyle size is by default: 16*(16 + 8) = 384*4 = 1536 bytes = 1.5 KB
+* guiStyle size is by default: 16*(16 + 8) = 384 int = 384*4 bytes = 1536 bytes = 1.5 KB
*
* Note that the first set of BASE properties (by default guiStyle[0..15]) belong to the generic style
* used for all controls, when any of those base values is set, it is automatically populated to all
-* controls, so, specific control values overwriting generic style should be set after base values.
+* controls, so, specific control values overwriting generic style should be set after base values
*
* After the first BASE set we have the EXTENDED properties (by default guiStyle[16..23]), those
* properties are actually common to all controls and can not be overwritten individually (like BASE ones)
@@ -100,7 +100,7 @@
* Every icon is codified in binary form, using 1 bit per pixel, so, every 16x16 icon
* requires 8 integers (16*16/32) to be stored in memory.
*
-* When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set.
+* When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set
*
* The global icons array size is fixed and depends on the number of icons and size:
*
@@ -112,20 +112,20 @@
*
* RAYGUI LAYOUT:
* raygui currently does not provide an auto-layout mechanism like other libraries,
-* layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it.
+* layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it
*
* TOOL: rGuiLayout is a visual tool to create raygui layouts: github.com/raysan5/rguilayout
*
* CONFIGURATION:
* #define RAYGUI_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 RAYGUI_STANDALONE
* Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined
* internally in the library and input management and drawing functions must be provided by
-* the user (check library implementation for further details).
+* the user (check library implementation for further details)
*
* #define RAYGUI_NO_ICONS
* Avoid including embedded ricons data (256 icons, 16x16 pixels, 1-bit per pixel, 2KB)
@@ -141,12 +141,17 @@
* Draw text bounds rectangles for debug
*
* VERSIONS HISTORY:
-* 4.5-dev (Sep-2024) Current dev version...
+* 5.0 (xx-Nov-2025) ADDED: Support up to 32 controls (v500)
* ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes
* ADDED: GuiValueBoxFloat()
* ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP
* ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH
+* ADDED: GuiLoadIconsFromMemory()
* ADDED: Multiple new icons
+* REMOVED: GuiSpinner() from controls list, using BUTTON + VALUEBOX properties
+* REMOVED: GuiSliderPro(), functionality was redundant
+* REVIEWED: Controls using text labels to use LABEL properties
+* REVIEWED: Replaced sprintf() by snprintf() for more safety
* REVIEWED: GuiTabBar(), close tab with mouse middle button
* REVIEWED: GuiScrollPanel(), scroll speed proportional to content
* REVIEWED: GuiDropdownBox(), support roll up and hidden arrow
@@ -156,6 +161,8 @@
* REVIEWED: GuiIconText(), increase buffer size and reviewed padding
* REVIEWED: GuiDrawText(), improved wrap mode drawing
* REVIEWED: GuiScrollBar(), minor tweaks
+* REVIEWED: GuiProgressBar(), improved borders computing
+* REVIEWED: GuiTextBox(), multiple improvements: autocursor and more
* REVIEWED: Functions descriptions, removed wrong return value reference
* REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion
*
@@ -259,16 +266,16 @@
* 1.4 (15-Jun-2017) Rewritten all GUI functions (removed useless ones)
* 1.3 (12-Jun-2017) Complete redesign of style system
* 1.1 (01-Jun-2017) Complete review of the library
-* 1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria.
-* 0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria.
-* 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria.
+* 1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria
+* 0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria
+* 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria
*
* DEPENDENCIES:
-* raylib 5.0 - Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing
+* raylib 5.6-dev - Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing
*
* STANDALONE MODE:
* By default raygui depends on raylib mostly for the inputs and the drawing functionality but that dependency can be disabled
-* with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs.
+* with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs
*
* The following functions should be redefined for a custom backend:
*
@@ -309,7 +316,7 @@
*
* LICENSE: zlib/libpng
*
-* Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+* 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.
@@ -334,7 +341,7 @@
#define RAYGUI_VERSION_MAJOR 4
#define RAYGUI_VERSION_MINOR 5
#define RAYGUI_VERSION_PATCH 0
-#define RAYGUI_VERSION "4.5-dev"
+#define RAYGUI_VERSION "5.0-dev"
#if !defined(RAYGUI_STANDALONE)
#include "raylib.h"
@@ -358,17 +365,6 @@
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
-// Allow custom memory allocators
-#ifndef RAYGUI_MALLOC
- #define RAYGUI_MALLOC(sz) malloc(sz)
-#endif
-#ifndef RAYGUI_CALLOC
- #define RAYGUI_CALLOC(n,sz) calloc(n,sz)
-#endif
-#ifndef RAYGUI_FREE
- #define RAYGUI_FREE(p) free(p)
-#endif
-
// Simple log system to avoid printf() calls if required
// NOTE: Avoiding those calls, also avoids const strings memory usage
#define RAYGUI_SUPPORT_LOG_INFO
@@ -421,13 +417,16 @@
// TODO: Texture2D type is very coupled to raylib, required by Font type
// It should be redesigned to be provided by user
- typedef struct Texture2D {
+ typedef struct Texture {
unsigned int id; // OpenGL texture id
int width; // Texture base width
int height; // Texture base height
int mipmaps; // Mipmap levels, 1 by default
int format; // Data format (PixelFormat type)
- } Texture2D;
+ } Texture;
+
+ // Texture2D, same as Texture
+ typedef Texture Texture2D;
// Image, pixel data stored in CPU memory (RAM)
typedef struct Image {
@@ -527,7 +526,7 @@ typedef enum {
DROPDOWNBOX,
TEXTBOX, // Used also for: TEXTBOXMULTI
VALUEBOX,
- SPINNER, // Uses: BUTTON, VALUEBOX
+ CONTROL11,
LISTVIEW,
COLORPICKER,
SCROLLBAR,
@@ -549,12 +548,12 @@ typedef enum {
BORDER_COLOR_DISABLED, // Control border color in STATE_DISABLED
BASE_COLOR_DISABLED, // Control base color in STATE_DISABLED
TEXT_COLOR_DISABLED, // Control text color in STATE_DISABLED
- BORDER_WIDTH, // Control border size, 0 for no border
+ BORDER_WIDTH = 12, // Control border size, 0 for no border
//TEXT_SIZE, // Control text size (glyphs max height) -> GLOBAL for all controls
//TEXT_SPACING, // Control text spacing between glyphs -> GLOBAL for all controls
- //TEXT_LINE_SPACING // Control text spacing between lines -> GLOBAL for all controls
- TEXT_PADDING, // Control text padding, not considering border
- TEXT_ALIGNMENT, // Control text horizontal alignment inside control text bound (after border and padding)
+ //TEXT_LINE_SPACING, // Control text spacing between lines -> GLOBAL for all controls
+ TEXT_PADDING = 13, // Control text padding, not considering border
+ TEXT_ALIGNMENT = 14, // Control text horizontal alignment inside control text bound (after border and padding)
//TEXT_WRAP_MODE // Control text wrap-mode inside text bounds -> GLOBAL for all controls
} GuiControlProperty;
@@ -641,11 +640,14 @@ typedef enum {
TEXT_READONLY = 16, // TextBox in read-only mode: 0-text editable, 1-text no-editable
} GuiTextBoxProperty;
-// Spinner
+// ValueBox/Spinner
typedef enum {
- SPIN_BUTTON_WIDTH = 16, // Spinner left/right buttons width
- SPIN_BUTTON_SPACING, // Spinner buttons separation
-} GuiSpinnerProperty;
+ SPINNER_BUTTON_WIDTH = 16, // Spinner left/right buttons width
+ SPINNER_BUTTON_SPACING, // Spinner buttons separation
+} GuiValueBoxProperty;
+
+// Control11
+//typedef enum { } GuiControl11Property;
// ListView
typedef enum {
@@ -653,6 +655,7 @@ typedef enum {
LIST_ITEMS_SPACING, // ListView items separation
SCROLLBAR_WIDTH, // ListView scrollbar size (usually width)
SCROLLBAR_SIDE, // ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE)
+ LIST_ITEMS_BORDER_NORMAL, // ListView items border enabled in normal state
LIST_ITEMS_BORDER_WIDTH // ListView items border width
} GuiListViewProperty;
@@ -717,6 +720,9 @@ RAYGUIAPI char **GuiLoadIcons(const char *fileName, bool loadIconsName); // Load
RAYGUIAPI void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color); // Draw icon using pixel size at specified position
#endif
+// Utility functions
+RAYGUIAPI int GuiGetTextWidth(const char *text); // Get text width considering gui style and icon size (if required)
+
// Controls
//----------------------------------------------------------------------------------------------------------
// Container/separator controls, useful for controls organization
@@ -999,33 +1005,33 @@ typedef enum {
ICON_MLAYERS = 226,
ICON_MAPS = 227,
ICON_HOT = 228,
- ICON_229 = 229,
- ICON_230 = 230,
- ICON_231 = 231,
- ICON_232 = 232,
- ICON_233 = 233,
- ICON_234 = 234,
- ICON_235 = 235,
- ICON_236 = 236,
- ICON_237 = 237,
- ICON_238 = 238,
- ICON_239 = 239,
- ICON_240 = 240,
- ICON_241 = 241,
- ICON_242 = 242,
- ICON_243 = 243,
- ICON_244 = 244,
- ICON_245 = 245,
- ICON_246 = 246,
- ICON_247 = 247,
- ICON_248 = 248,
- ICON_249 = 249,
+ ICON_LABEL = 229,
+ ICON_NAME_ID = 230,
+ ICON_SLICING = 231,
+ ICON_MANUAL_CONTROL = 232,
+ ICON_COLLISION = 233,
+ ICON_CIRCLE_ADD = 234,
+ ICON_CIRCLE_ADD_FILL = 235,
+ ICON_CIRCLE_WARNING = 236,
+ ICON_CIRCLE_WARNING_FILL = 237,
+ ICON_BOX_MORE = 238,
+ ICON_BOX_MORE_FILL = 239,
+ ICON_BOX_MINUS = 240,
+ ICON_BOX_MINUS_FILL = 241,
+ ICON_UNION = 242,
+ ICON_INTERSECTION = 243,
+ ICON_DIFFERENCE = 244,
+ ICON_SPHERE = 245,
+ ICON_CYLINDER = 246,
+ ICON_CONE = 247,
+ ICON_ELLIPSOID = 248,
+ ICON_CAPSULE = 249,
ICON_250 = 250,
ICON_251 = 251,
ICON_252 = 252,
ICON_253 = 253,
ICON_254 = 254,
- ICON_255 = 255,
+ ICON_255 = 255
} GuiIconName;
#endif
@@ -1046,12 +1052,24 @@ typedef enum {
#if defined(RAYGUI_IMPLEMENTATION)
#include // required for: isspace() [GuiTextBox()]
-#include // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()]
-#include // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]
+#include // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), snprintf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()]
#include // Required for: strlen() [GuiTextBox(), GuiValueBox()], memset(), memcpy()
#include // Required for: va_list, va_start(), vfprintf(), va_end() [TextFormat()]
#include // Required for: roundf() [GuiColorPicker()]
+// Allow custom memory allocators
+#if defined(RAYGUI_MALLOC) || defined(RAYGUI_CALLOC) || defined(RAYGUI_FREE)
+ #if !defined(RAYGUI_MALLOC) || !defined(RAYGUI_CALLOC) || !defined(RAYGUI_FREE)
+ #error "RAYGUI: if RAYGUI_MALLOC, RAYGUI_CALLOC, or RAYGUI_FREE is customized, all three must be customized"
+ #endif
+#else
+ #include // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]
+
+ #define RAYGUI_MALLOC(sz) malloc(sz)
+ #define RAYGUI_CALLOC(n,sz) calloc(n,sz)
+ #define RAYGUI_FREE(p) free(p)
+#endif
+
#ifdef __cplusplus
#define RAYGUI_CLITERAL(name) name
#else
@@ -1060,7 +1078,7 @@ typedef enum {
// Check if two rectangles are equal, used to validate a slider bounds as an id
#ifndef CHECK_BOUNDS_ID
- #define CHECK_BOUNDS_ID(src, dst) ((src.x == dst.x) && (src.y == dst.y) && (src.width == dst.width) && (src.height == dst.height))
+ #define CHECK_BOUNDS_ID(src, dst) (((int)src.x == (int)dst.x) && ((int)src.y == (int)dst.y) && ((int)src.width == (int)dst.width) && ((int)src.height == (int)dst.height))
#endif
#if !defined(RAYGUI_NO_ICONS) && !defined(RAYGUI_CUSTOM_ICONS)
@@ -1318,27 +1336,27 @@ static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] =
0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x422a422a, 0x422e422a, 0x40384e28, 0x00007fe0, // ICON_MLAYERS
0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x5b2a512a, 0x512e552a, 0x40385128, 0x00007fe0, // ICON_MAPS
0x04200000, 0x1cf00c60, 0x11f019f0, 0x0f3807b8, 0x1e3c0f3c, 0x1c1c1e1c, 0x1e3c1c1c, 0x00000f70, // ICON_HOT
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_229
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_230
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_231
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_232
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_233
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_234
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_235
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_236
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_237
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_238
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_239
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_240
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_241
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_242
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_243
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_244
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_245
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_246
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_247
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_248
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_249
+ 0x00000000, 0x20803f00, 0x2a202e40, 0x20082e10, 0x08021004, 0x02040402, 0x00900108, 0x00000060, // ICON_LABEL
+ 0x00000000, 0x042007e0, 0x47e27c3e, 0x4ffa4002, 0x47fa4002, 0x4ffa4002, 0x7ffe4002, 0x00000000, // ICON_NAME_ID
+ 0x7fe00000, 0x402e4020, 0x43ce5e0a, 0x40504078, 0x438e4078, 0x402e5e0a, 0x7fe04020, 0x00000000, // ICON_SLICING
+ 0x00000000, 0x40027ffe, 0x47c24002, 0x55425d42, 0x55725542, 0x50125552, 0x10105016, 0x00001ff0, // ICON_MANUAL_CONTROL
+ 0x7ffe0000, 0x43c24002, 0x48124422, 0x500a500a, 0x500a500a, 0x44224812, 0x400243c2, 0x00007ffe, // ICON_COLLISION
+ 0x03c00000, 0x10080c30, 0x21842184, 0x4ff24182, 0x41824ff2, 0x21842184, 0x0c301008, 0x000003c0, // ICON_CIRCLE_ADD
+ 0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x700e7e7e, 0x7e7e700e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0, // ICON_CIRCLE_ADD_FILL
+ 0x03c00000, 0x10080c30, 0x21842184, 0x41824182, 0x40024182, 0x21842184, 0x0c301008, 0x000003c0, // ICON_CIRCLE_WARNING
+ 0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x7e7e7e7e, 0x7ffe7e7e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0, // ICON_CIRCLE_WARNING_FILL
+ 0x00000000, 0x10041ffc, 0x10841004, 0x13e41084, 0x10841084, 0x10041004, 0x00001ffc, 0x00000000, // ICON_BOX_MORE
+ 0x00000000, 0x1ffc1ffc, 0x1f7c1ffc, 0x1c1c1f7c, 0x1f7c1f7c, 0x1ffc1ffc, 0x00001ffc, 0x00000000, // ICON_BOX_MORE_FILL
+ 0x00000000, 0x1ffc1ffc, 0x1ffc1ffc, 0x1c1c1ffc, 0x1ffc1ffc, 0x1ffc1ffc, 0x00001ffc, 0x00000000, // ICON_BOX_MINUS
+ 0x00000000, 0x10041ffc, 0x10041004, 0x13e41004, 0x10041004, 0x10041004, 0x00001ffc, 0x00000000, // ICON_BOX_MINUS_FILL
+ 0x07fe0000, 0x055606aa, 0x7ff606aa, 0x55766eba, 0x55766eaa, 0x55606ffe, 0x55606aa0, 0x00007fe0, // ICON_UNION
+ 0x07fe0000, 0x04020402, 0x7fe20402, 0x456246a2, 0x456246a2, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_INTERSECTION
+ 0x07fe0000, 0x055606aa, 0x7ff606aa, 0x4436442a, 0x4436442a, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_DIFFERENCE
+ 0x03c00000, 0x10080c30, 0x20042004, 0x60064002, 0x47e2581a, 0x20042004, 0x0c301008, 0x000003c0, // ICON_SPHERE
+ 0x03e00000, 0x08080410, 0x0c180808, 0x08080be8, 0x08080808, 0x08080808, 0x04100808, 0x000003e0, // ICON_CYLINDER
+ 0x00800000, 0x01400140, 0x02200220, 0x04100410, 0x08080808, 0x1c1c13e4, 0x08081004, 0x000007f0, // ICON_CONE
+ 0x00000000, 0x07e00000, 0x20841918, 0x40824082, 0x40824082, 0x19182084, 0x000007e0, 0x00000000, // ICON_ELLIPSOID
+ 0x00000000, 0x00000000, 0x20041ff8, 0x40024002, 0x40024002, 0x1ff82004, 0x00000000, 0x00000000, // ICON_CAPSULE
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_250
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_251
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_252
@@ -1363,7 +1381,7 @@ static unsigned int *guiIconsPtr = guiIcons;
#define RAYGUI_MAX_PROPS_EXTENDED 8 // Maximum number of extended properties
//----------------------------------------------------------------------------------
-// Types and Structures Definition
+// Module Types and Structures Definition
//----------------------------------------------------------------------------------
// Gui control property style color element
typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement;
@@ -1387,8 +1405,7 @@ static Rectangle guiControlExclusiveRec = { 0 }; // Gui control exclusive bounds
static int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*()
//static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking
-static int autoCursorCooldownCounter = 0; // Cooldown frame counter for automatic cursor movement on key-down
-static int autoCursorDelayCounter = 0; // Delay frame counter for automatic cursor movement
+static int autoCursorCounter = 0; // Frame counter for automatic repeated cursor movement on key-down (cooldown and delay)
//----------------------------------------------------------------------------------
// Style data array for all gui style properties (allocated on data segment by default)
@@ -1484,7 +1501,6 @@ static void DrawRectangleGradientV(int posX, int posY, int width, int height, Co
//----------------------------------------------------------------------------------
static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize); // Load style from memory (binary only)
-static int GetTextWidth(const char *text); // Gui get text width using gui font and style
static Rectangle GetTextBounds(int control, Rectangle bounds); // Get text bounds considering control bounds
static const char *GetTextIcon(const char *text, int *iconId); // Get text icon if provided and move text cursor
@@ -1589,6 +1605,10 @@ int GuiWindowBox(Rectangle bounds, const char *title)
#define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT 24
#endif
+ #if !defined(RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT)
+ #define RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT 18
+ #endif
+
int result = 0;
//GuiState state = guiState;
@@ -1597,9 +1617,10 @@ int GuiWindowBox(Rectangle bounds, const char *title)
Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)statusBarHeight };
if (bounds.height < statusBarHeight*2.0f) bounds.height = statusBarHeight*2.0f;
+ const float vPadding = statusBarHeight/2.0f - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT/2.0f;
Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - 1, bounds.width, bounds.height - (float)statusBarHeight + 1 };
- Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - 20,
- statusBar.y + statusBarHeight/2.0f - 18.0f/2.0f, 18, 18 };
+ Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding,
+ statusBar.y + vPadding, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT };
// Update control
//--------------------------------------------------------------------
@@ -1653,7 +1674,7 @@ int GuiGroupBox(Rectangle bounds, const char *text)
// Line control
int GuiLine(Rectangle bounds, const char *text)
{
- #if !defined(RAYGUI_LINE_ORIGIN_SIZE)
+ #if !defined(RAYGUI_LINE_MARGIN_TEXT)
#define RAYGUI_LINE_MARGIN_TEXT 12
#endif
#if !defined(RAYGUI_LINE_TEXT_PADDING)
@@ -1671,7 +1692,7 @@ int GuiLine(Rectangle bounds, const char *text)
else
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(text) + 2;
+ textBounds.width = (float)GuiGetTextWidth(text) + 2;
textBounds.height = bounds.height;
textBounds.x = bounds.x + RAYGUI_LINE_MARGIN_TEXT;
textBounds.y = bounds.y;
@@ -1711,8 +1732,8 @@ int GuiPanel(Rectangle bounds, const char *text)
//--------------------------------------------------------------------
if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar
- GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED: (int)LINE_COLOR)),
- GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR)));
+ GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)),
+ GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BASE_COLOR_DISABLED : (int)BACKGROUND_COLOR)));
//--------------------------------------------------------------------
return result;
@@ -1722,7 +1743,7 @@ int GuiPanel(Rectangle bounds, const char *text)
// NOTE: Using GuiToggle() for the TABS
int GuiTabBar(Rectangle bounds, const char **text, int count, int *active)
{
- #define RAYGUI_TABBAR_ITEM_WIDTH 160
+ #define RAYGUI_TABBAR_ITEM_WIDTH 148
int result = -1;
//GuiState state = guiState;
@@ -1755,12 +1776,12 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active)
if (i == (*active))
{
toggle = true;
- GuiToggle(tabBounds, GuiIconText(12, text[i]), &toggle);
+ GuiToggle(tabBounds, text[i], &toggle);
}
else
{
toggle = false;
- GuiToggle(tabBounds, GuiIconText(12, text[i]), &toggle);
+ GuiToggle(tabBounds, text[i], &toggle);
if (toggle) *active = i;
}
@@ -2011,7 +2032,7 @@ int GuiLabelButton(Rectangle bounds, const char *text)
bool pressed = false;
// NOTE: We force bounds.width to be all text
- float textWidth = (float)GetTextWidth(text);
+ float textWidth = (float)GuiGetTextWidth(text);
if ((bounds.width - 2*GuiGetStyle(LABEL, BORDER_WIDTH) - 2*GuiGetStyle(LABEL, TEXT_PADDING)) < textWidth) bounds.width = textWidth + 2*GuiGetStyle(LABEL, BORDER_WIDTH) + 2*GuiGetStyle(LABEL, TEXT_PADDING) + 2;
// Update control
@@ -2149,7 +2170,9 @@ int GuiToggleSlider(Rectangle bounds, const char *text, int *active)
// Get substrings items from text (items pointers)
int itemCount = 0;
- const char **items = GuiTextSplit(text, ';', &itemCount, NULL);
+ const char **items = NULL;
+
+ if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL);
Rectangle slider = {
0, // Calculated later depending on the active toggle
@@ -2196,7 +2219,7 @@ int GuiToggleSlider(Rectangle bounds, const char *text, int *active)
if (text != NULL)
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(text);
+ textBounds.width = (float)GuiGetTextWidth(text);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = slider.x + slider.width/2 - textBounds.width/2;
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -2221,7 +2244,7 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked)
if (text != NULL)
{
- textBounds.width = (float)GetTextWidth(text) + 2;
+ textBounds.width = (float)GuiGetTextWidth(text) + 2;
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -2474,7 +2497,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod
int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
{
#if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)
- #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 40 // Frames to wait for autocursor movement
+ #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 20 // Frames to wait for autocursor movement
#endif
#if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY)
#define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY 1 // Frames delay for autocursor movement
@@ -2487,10 +2510,10 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE);
Rectangle textBounds = GetTextBounds(TEXTBOX, bounds);
- int textLength = (int)strlen(text); // Get current text length
+ int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length
int thisCursorIndex = textBoxCursorIndex;
if (thisCursorIndex > textLength) thisCursorIndex = textLength;
- int textWidth = GetTextWidth(text) - GetTextWidth(text + thisCursorIndex);
+ int textWidth = GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex);
int textIndexOffset = 0; // Text index offset to start drawing in the box
// Cursor rectangle
@@ -2511,15 +2534,6 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
mouseCursor.x = -1;
mouseCursor.width = 1;
- // Auto-cursor movement logic
- // NOTE: Cursor moves automatically when key down after some time
- if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCooldownCounter++;
- else
- {
- autoCursorCooldownCounter = 0; // GLOBAL: Cursor cooldown counter
- autoCursorDelayCounter = 0; // GLOBAL: Cursor delay counter
- }
-
// Blink-cursor frame counter
//if (!autoCursorMode) blinkCursorFrameCounter++;
//else blinkCursorFrameCounter = 0;
@@ -2537,6 +2551,13 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
if (editMode)
{
+ // GLOBAL: Auto-cursor movement logic
+ // NOTE: Keystrokes are handled repeatedly when button is held down for some time
+ if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCounter++;
+ else autoCursorCounter = 0;
+
+ bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0);
+
state = STATE_PRESSED;
if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
@@ -2550,7 +2571,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
textIndexOffset += nextCodepointSize;
- textWidth = GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex);
+ textWidth = GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex);
}
int codepoint = GetCharPressed(); // Get Unicode codepoint
@@ -2560,10 +2581,43 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
int codepointSize = 0;
const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize);
- // Add codepoint to text, at current cursor position
- // NOTE: Make sure we do not overflow buffer size
- if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize))
+ // Handle text paste action
+ if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
{
+ const char *pasteText = GetClipboardText();
+ if (pasteText != NULL)
+ {
+ int pasteLength = 0;
+ int pasteCodepoint;
+ int pasteCodepointSize;
+
+ // Count how many codepoints to copy, stopping at the first unwanted control character
+ while (true)
+ {
+ pasteCodepoint = GetCodepointNext(pasteText + pasteLength, &pasteCodepointSize);
+ if (textLength + pasteLength + pasteCodepointSize >= textSize) break;
+ if (!(multiline && (pasteCodepoint == (int)'\n')) && !(pasteCodepoint >= 32)) break;
+ pasteLength += pasteCodepointSize;
+ }
+
+ if (pasteLength > 0)
+ {
+ // Move forward data from cursor position
+ for (int i = textLength + pasteLength; i > textBoxCursorIndex; i--) text[i] = text[i - pasteLength];
+
+ // Paste data in at cursor
+ for (int i = 0; i < pasteLength; i++) text[textBoxCursorIndex + i] = pasteText[i];
+
+ textBoxCursorIndex += pasteLength;
+ textLength += pasteLength;
+ text[textLength] = '\0';
+ }
+ }
+ }
+ else if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize))
+ {
+ // Adding codepoint to text, at current cursor position
+
// Move forward data from cursor position
for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize];
@@ -2583,113 +2637,185 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
// Move cursor to end
if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) textBoxCursorIndex = textLength;
- // Delete codepoint from text, after current cursor position
- if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))))
+ // Delete related codepoints from text, after current cursor position
+ if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
{
- autoCursorDelayCounter++;
+ int offset = textBoxCursorIndex;
+ int accCodepointSize = 0;
+ int nextCodepointSize;
+ int nextCodepoint;
- if (IsKeyPressed(KEY_DELETE) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames
+ // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
+ // Not using isalnum() since it only works on ASCII characters
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+ bool puctuation = ispunct(nextCodepoint & 0xff);
+ while (offset < textLength)
{
- int nextCodepointSize = 0;
- GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
-
- // Move backward text from cursor position
- for (int i = textBoxCursorIndex; i < textLength; i++) text[i] = text[i + nextCodepointSize];
-
- textLength -= codepointSize;
- if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
-
- // Make sure text last character is EOL
- text[textLength] = '\0';
+ if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff))))
+ break;
+ offset += nextCodepointSize;
+ accCodepointSize += nextCodepointSize;
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
}
+
+ // Check whitespace to delete (ASCII only)
+ while (offset < textLength)
+ {
+ if (!isspace(nextCodepoint & 0xff)) break;
+
+ offset += nextCodepointSize;
+ accCodepointSize += nextCodepointSize;
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+ }
+
+ // Move text after cursor forward (including final null terminator)
+ for (int i = offset; i <= textLength; i++) text[i - accCodepointSize] = text[i];
+
+ textLength -= accCodepointSize;
+ }
+
+ else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger)))
+ {
+ // Delete single codepoint from text, after current cursor position
+
+ int nextCodepointSize = 0;
+ GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+
+ // Move text after cursor forward (including final null terminator)
+ for (int i = textBoxCursorIndex + nextCodepointSize; i <= textLength; i++) text[i - nextCodepointSize] = text[i];
+
+ textLength -= nextCodepointSize;
}
// Delete related codepoints from text, before current cursor position
- if ((textLength > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+ if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
{
- int i = textBoxCursorIndex - 1;
+ int offset = textBoxCursorIndex;
int accCodepointSize = 0;
+ int prevCodepointSize;
+ int prevCodepoint;
- // Move cursor to the end of word if on space already
- while ((i > 0) && isspace(text[i]))
+ // Check whitespace to delete (ASCII only)
+ while (offset > 0)
{
- int prevCodepointSize = 0;
- GetCodepointPrevious(text + i, &prevCodepointSize);
- i -= prevCodepointSize;
+ prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+ if (!isspace(prevCodepoint & 0xff)) break;
+
+ offset -= prevCodepointSize;
accCodepointSize += prevCodepointSize;
}
- // Move cursor to the start of the word
- while ((i > 0) && !isspace(text[i]))
+ // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
+ // Not using isalnum() since it only works on ASCII characters
+ bool puctuation = ispunct(prevCodepoint & 0xff);
+ while (offset > 0)
{
- int prevCodepointSize = 0;
- GetCodepointPrevious(text + i, &prevCodepointSize);
- i -= prevCodepointSize;
+ prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+ if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
+
+ offset -= prevCodepointSize;
accCodepointSize += prevCodepointSize;
}
- // Move forward text from cursor position
- for (int j = (textBoxCursorIndex - accCodepointSize); j < textLength; j++) text[j] = text[j + accCodepointSize];
+ // Move text after cursor forward (including final null terminator)
+ for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - accCodepointSize] = text[i];
- // Prevent cursor index from decrementing past 0
- if (textBoxCursorIndex > 0)
- {
- textBoxCursorIndex -= accCodepointSize;
- textLength -= accCodepointSize;
- }
+ textLength -= accCodepointSize;
+ textBoxCursorIndex -= accCodepointSize;
+ }
- // Make sure text last character is EOL
- text[textLength] = '\0';
- }
- else if ((textLength > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))))
+ else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger)))
{
- autoCursorDelayCounter++;
+ // Delete single codepoint from text, before current cursor position
- if (IsKeyPressed(KEY_BACKSPACE) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames
- {
- int prevCodepointSize = 0;
+ int prevCodepointSize = 0;
- // Prevent cursor index from decrementing past 0
- if (textBoxCursorIndex > 0)
- {
- GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+ GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
- // Move backward text from cursor position
- for (int i = (textBoxCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize];
+ // Move text after cursor forward (including final null terminator)
+ for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - prevCodepointSize] = text[i];
- textBoxCursorIndex -= codepointSize;
- textLength -= codepointSize;
- }
-
- // Make sure text last character is EOL
- text[textLength] = '\0';
- }
+ textLength -= prevCodepointSize;
+ textBoxCursorIndex -= prevCodepointSize;
}
// Move cursor position with keys
- if (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))
+ if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
{
- autoCursorDelayCounter++;
+ int offset = textBoxCursorIndex;
+ //int accCodepointSize = 0;
+ int prevCodepointSize;
+ int prevCodepoint;
- if (IsKeyPressed(KEY_LEFT) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames
+ // Check whitespace to skip (ASCII only)
+ while (offset > 0)
{
- int prevCodepointSize = 0;
- if (textBoxCursorIndex > 0) GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+ prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+ if (!isspace(prevCodepoint & 0xff)) break;
- if (textBoxCursorIndex >= prevCodepointSize) textBoxCursorIndex -= prevCodepointSize;
+ offset -= prevCodepointSize;
+ //accCodepointSize += prevCodepointSize;
}
+
+ // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
+ // Not using isalnum() since it only works on ASCII characters
+ bool puctuation = ispunct(prevCodepoint & 0xff);
+ while (offset > 0)
+ {
+ prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+ if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
+
+ offset -= prevCodepointSize;
+ //accCodepointSize += prevCodepointSize;
+ }
+
+ textBoxCursorIndex = offset;
}
- else if (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))
+ else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger)))
{
- autoCursorDelayCounter++;
+ int prevCodepointSize = 0;
+ GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
- if (IsKeyPressed(KEY_RIGHT) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames
+ textBoxCursorIndex -= prevCodepointSize;
+ }
+ else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+ {
+ int offset = textBoxCursorIndex;
+ //int accCodepointSize = 0;
+ int nextCodepointSize;
+ int nextCodepoint;
+
+ // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
+ // Not using isalnum() since it only works on ASCII characters
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+ bool puctuation = ispunct(nextCodepoint & 0xff);
+ while (offset < textLength)
{
- int nextCodepointSize = 0;
- GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+ if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) break;
- if ((textBoxCursorIndex + nextCodepointSize) <= textLength) textBoxCursorIndex += nextCodepointSize;
+ offset += nextCodepointSize;
+ //accCodepointSize += nextCodepointSize;
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
}
+
+ // Check whitespace to skip (ASCII only)
+ while (offset < textLength)
+ {
+ if (!isspace(nextCodepoint & 0xff)) break;
+
+ offset += nextCodepointSize;
+ //accCodepointSize += nextCodepointSize;
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+ }
+
+ textBoxCursorIndex = offset;
+ }
+ else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger)))
+ {
+ int nextCodepointSize = 0;
+ GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+
+ textBoxCursorIndex += nextCodepointSize;
}
// Move cursor position with mouse
@@ -2701,7 +2827,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
float widthToMouseX = 0;
int mouseCursorIndex = 0;
- for (int i = textIndexOffset; i < textLength; i++)
+ for (int i = textIndexOffset; i < textLength; i += codepointSize)
{
codepoint = GetCodepointNext(&text[i], &codepointSize);
codepointIndex = GetGlyphIndex(guiFont, codepoint);
@@ -2720,7 +2846,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
}
// Check if mouse cursor is at the last position
- int textEndWidth = GetTextWidth(text + textIndexOffset);
+ int textEndWidth = GuiGetTextWidth(text + textIndexOffset);
if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2))
{
mouseCursor.x = textBounds.x + textEndWidth;
@@ -2737,7 +2863,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
else mouseCursor.x = -1;
// Recalculate cursor position.y depending on textBoxCursorIndex
- cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
+ cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
//if (multiline) cursor.y = GetTextLines()
// Finish text editing on ENTER or mouse click outside bounds
@@ -2745,6 +2871,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
(!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
{
textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index
+ autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes
result = 1;
}
}
@@ -2757,6 +2884,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
{
textBoxCursorIndex = textLength; // GLOBAL: Place cursor index to the end of current text
+ autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes
result = 1;
}
}
@@ -2825,19 +2953,22 @@ int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int
int tempValue = *value;
- Rectangle spinner = { bounds.x + GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SPIN_BUTTON_SPACING), bounds.y,
- bounds.width - 2*(GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SPIN_BUTTON_SPACING)), bounds.height };
- Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.height };
- Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.y, (float)GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.height };
+ Rectangle valueBoxBounds = {
+ bounds.x + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING),
+ bounds.y,
+ bounds.width - 2*(GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING)), bounds.height };
+ Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height };
+ Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.y,
+ (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height };
Rectangle textBounds = { 0 };
if (text != NULL)
{
- textBounds.width = (float)GetTextWidth(text) + 2;
+ textBounds.width = (float)GuiGetTextWidth(text) + 2;
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
- textBounds.x = bounds.x + bounds.width + GuiGetStyle(SPINNER, TEXT_PADDING);
+ textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
- if (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SPINNER, TEXT_PADDING);
+ if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
}
// Update control
@@ -2871,20 +3002,20 @@ int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int
// Draw control
//--------------------------------------------------------------------
- result = GuiValueBox(spinner, NULL, &tempValue, minValue, maxValue, editMode);
+ result = GuiValueBox(valueBoxBounds, NULL, &tempValue, minValue, maxValue, editMode);
// Draw value selector custom buttons
// NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
- GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(SPINNER, BORDER_WIDTH));
+ GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(VALUEBOX, BORDER_WIDTH));
GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
// Draw text label if provided
- GuiDrawText(text, textBounds, (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+ GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
//--------------------------------------------------------------------
*value = tempValue;
@@ -2902,13 +3033,13 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in
int result = 0;
GuiState state = guiState;
- char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0";
- sprintf(textValue, "%i", *value);
+ char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = { 0 };
+ snprintf(textValue, RAYGUI_VALUEBOX_MAX_CHARS + 1, "%i", *value);
Rectangle textBounds = { 0 };
if (text != NULL)
{
- textBounds.width = (float)GetTextWidth(text) + 2;
+ textBounds.width = (float)GuiGetTextWidth(text) + 2;
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -2920,7 +3051,6 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in
if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
{
Vector2 mousePoint = GetMousePosition();
-
bool valueHasChanged = false;
if (editMode)
@@ -2929,30 +3059,53 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in
int keyCount = (int)strlen(textValue);
- // Only allow keys in range [48..57]
- if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+ // Add or remove minus symbol
+ if (IsKeyPressed(KEY_MINUS))
{
- if (GetTextWidth(textValue) < bounds.width)
+ if (textValue[0] == '-')
{
- int key = GetCharPressed();
- if ((key >= 48) && (key <= 57))
+ for (int i = 0 ; i < keyCount; i++) textValue[i] = textValue[i + 1];
+
+ keyCount--;
+ valueHasChanged = true;
+ }
+ else if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+ {
+ if (keyCount == 0)
{
- textValue[keyCount] = (char)key;
+ textValue[0] = '0';
+ textValue[1] = '\0';
keyCount++;
- valueHasChanged = true;
}
+
+ for (int i = keyCount ; i > -1; i--) textValue[i + 1] = textValue[i];
+
+ textValue[0] = '-';
+ keyCount++;
+ valueHasChanged = true;
+ }
+ }
+
+ // Add new digit to text value
+ if ((keyCount >= 0) && (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) && (GuiGetTextWidth(textValue) < bounds.width))
+ {
+ int key = GetCharPressed();
+
+ // Only allow keys in range [48..57]
+ if ((key >= 48) && (key <= 57))
+ {
+ textValue[keyCount] = (char)key;
+ keyCount++;
+ valueHasChanged = true;
}
}
// Delete text
- if (keyCount > 0)
+ if ((keyCount > 0) && IsKeyPressed(KEY_BACKSPACE))
{
- if (IsKeyPressed(KEY_BACKSPACE))
- {
- keyCount--;
- textValue[keyCount] = '\0';
- valueHasChanged = true;
- }
+ keyCount--;
+ textValue[keyCount] = '\0';
+ valueHasChanged = true;
}
if (valueHasChanged) *value = TextToInteger(textValue);
@@ -2992,11 +3145,14 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in
GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor);
GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))));
- // Draw cursor
+ // Draw cursor rectangle
if (editMode)
{
// NOTE: ValueBox internal text is always centered
- Rectangle cursor = { bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 1, bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4, bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH) };
+ Rectangle cursor = { bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1,
+ bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + 2,
+ 2, bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2 - 4 };
+ if (cursor.height > bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2;
GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
}
@@ -3019,12 +3175,12 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float
GuiState state = guiState;
//char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0";
- //sprintf(textValue, "%2.2f", *value);
+ //snprintf(textValue, sizeof(textValue), "%2.2f", *value);
- Rectangle textBounds = {0};
+ Rectangle textBounds = { 0 };
if (text != NULL)
{
- textBounds.width = (float)GetTextWidth(text) + 2;
+ textBounds.width = (float)GuiGetTextWidth(text) + 2;
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -3045,10 +3201,37 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float
int keyCount = (int)strlen(textValue);
+ // Add or remove minus symbol
+ if (IsKeyPressed(KEY_MINUS))
+ {
+ if (textValue[0] == '-')
+ {
+ for (int i = 0; i < keyCount; i++) textValue[i] = textValue[i + 1];
+
+ keyCount--;
+ valueHasChanged = true;
+ }
+ else if (keyCount < (RAYGUI_VALUEBOX_MAX_CHARS - 1))
+ {
+ if (keyCount == 0)
+ {
+ textValue[0] = '0';
+ textValue[1] = '\0';
+ keyCount++;
+ }
+
+ for (int i = keyCount; i > -1; i--) textValue[i + 1] = textValue[i];
+
+ textValue[0] = '-';
+ keyCount++;
+ valueHasChanged = true;
+ }
+ }
+
// Only allow keys in range [48..57]
if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
{
- if (GetTextWidth(textValue) < bounds.width)
+ if (GuiGetTextWidth(textValue) < bounds.width)
{
int key = GetCharPressed();
if (((key >= 48) && (key <= 57)) ||
@@ -3103,7 +3286,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float
if (editMode)
{
// NOTE: ValueBox internal text is always centered
- Rectangle cursor = {bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 1,
+ Rectangle cursor = {bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1,
bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4,
bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH)};
GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
@@ -3120,7 +3303,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float
// Slider control with pro parameters
// NOTE: Other GuiSlider*() controls use this one
-int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth)
+int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
{
int result = 0;
GuiState state = guiState;
@@ -3129,6 +3312,8 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight,
if (value == NULL) value = &temp;
float oldValue = *value;
+ int sliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH);
+
Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) };
@@ -3146,7 +3331,7 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight,
{
state = STATE_PRESSED;
// Get equivalent value and slider position from mousePosition.x
- *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width-sliderWidth)) + minValue;
+ *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue;
}
}
else
@@ -3166,7 +3351,7 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight,
if (!CheckCollisionPointRec(mousePoint, slider))
{
// Get equivalent value and slider position from mousePosition.x
- *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width-sliderWidth)) + minValue;
+ *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue;
}
}
else state = STATE_FOCUSED;
@@ -3205,44 +3390,45 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight,
if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)));
else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)));
else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_PRESSED)));
+ else if (state == STATE_DISABLED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_DISABLED)));
// Draw left/right text if provided
if (textLeft != NULL)
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(textLeft);
+ textBounds.width = (float)GuiGetTextWidth(textLeft);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
- GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))));
+ GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
}
if (textRight != NULL)
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(textRight);
+ textBounds.width = (float)GuiGetTextWidth(textRight);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
- GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))));
+ GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
}
//--------------------------------------------------------------------
return result;
}
-// Slider control extended, returns selected value and has text
-int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
-{
- return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH));
-}
-
// Slider Bar control extended, returns selected value
int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
{
- return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, 0);
+ int result = 0;
+ int preSliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH);
+ GuiSetStyle(SLIDER, SLIDER_WIDTH, 0);
+ result = GuiSlider(bounds, textLeft, textRight, value, minValue, maxValue);
+ GuiSetStyle(SLIDER, SLIDER_WIDTH, preSliderWidth);
+
+ return result;
}
// Progress Bar control extended, shows current progress value
@@ -3257,14 +3443,14 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight
// Progress bar
Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH),
bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), 0,
- bounds.height - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) };
+ bounds.height - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) -1 };
// Update control
//--------------------------------------------------------------------
if (*value > maxValue) *value = maxValue;
// WARNING: Working with floats could lead to rounding issues
- if ((state != STATE_DISABLED)) progress.width = (float)(*value/(maxValue - minValue))*bounds.width - ((*value >= maxValue)? (float)(2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)) : 0.0f);
+ if ((state != STATE_DISABLED)) progress.width = ((float)*value/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH));
//--------------------------------------------------------------------
// Draw control
@@ -3282,15 +3468,15 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight
GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
}
- else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+ else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
- if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + 1, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+ if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1}, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
else
{
// Draw borders not yet reached by value
- GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + 1, bounds.y, bounds.width - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
- GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + 1, bounds.y + bounds.height - 1, bounds.width - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
- GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+ GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+ GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y + bounds.height - 1, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+ GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
}
// Draw slider internal progress bar (depends on state)
@@ -3301,23 +3487,23 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight
if (textLeft != NULL)
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(textLeft);
+ textBounds.width = (float)GuiGetTextWidth(textLeft);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x - textBounds.width - GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
- GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))));
+ GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
}
if (textRight != NULL)
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(textRight);
+ textBounds.width = (float)GuiGetTextWidth(textRight);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
- GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))));
+ GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
}
//--------------------------------------------------------------------
@@ -3467,11 +3653,11 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd
// Draw visible items
for (int i = 0; ((i < visibleItems) && (text != NULL)); i++)
{
- GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK);
+ if (GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_NORMAL)) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK);
if (state == STATE_DISABLED)
{
- if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)));
+ if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)));
GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)));
}
@@ -3480,18 +3666,18 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd
if (((startIndex + i) == itemSelected) && (active != NULL))
{
// Draw item selected
- GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)));
+ GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)));
GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED)));
}
else if (((startIndex + i) == itemFocused)) // && (focus != NULL)) // NOTE: We want items focused, despite not returned!
{
// Draw item focused
- GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)));
+ GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)));
GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED)));
}
else
{
- // Draw item normal
+ // Draw item normal (no rectangle)
GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)));
}
}
@@ -3531,22 +3717,22 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd
return result;
}
-// Color Panel control - Color (RGBA) variant.
+// Color Panel control - Color (RGBA) variant
int GuiColorPanel(Rectangle bounds, const char *text, Color *color)
{
int result = 0;
Vector3 vcolor = { (float)color->r/255.0f, (float)color->g/255.0f, (float)color->b/255.0f };
Vector3 hsv = ConvertRGBtoHSV(vcolor);
- Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv.
+ Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv
GuiColorPanelHSV(bounds, text, &hsv);
- // Check if the hsv was changed, only then change the color.
- // This is required, because the Color->HSV->Color conversion has precision errors.
- // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value.
- // Otherwise GuiColorPanel would often modify it's color without user input.
- // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check.
+ // Check if the hsv was changed, only then change the color
+ // This is required, because the Color->HSV->Color conversion has precision errors
+ // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value
+ // Otherwise GuiColorPanel would often modify it's color without user input
+ // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check
if (hsv.x != prevHsv.x || hsv.y != prevHsv.y || hsv.z != prevHsv.z)
{
Vector3 rgb = ConvertHSVtoRGB(hsv);
@@ -3570,7 +3756,10 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
int result = 0;
GuiState state = guiState;
- Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT), (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 };
+ Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2,
+ (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW),
+ (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT),
+ (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 };
// Update control
//--------------------------------------------------------------------
@@ -3617,7 +3806,6 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
// Draw control
//--------------------------------------------------------------------
-
// Draw alpha bar: checked background
if (state != STATE_DISABLED)
{
@@ -3755,7 +3943,7 @@ int GuiColorPicker(Rectangle bounds, const char *text, Color *color)
Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height };
//Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) };
- // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used.
+ // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used
Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ (*color).r/255.0f, (*color).g/255.0f, (*color).b/255.0f });
GuiColorBarHue(boundsHue, NULL, &hsv.x);
@@ -3768,8 +3956,8 @@ int GuiColorPicker(Rectangle bounds, const char *text, Color *color)
return result;
}
-// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering.
-// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB.
+// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering
+// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB
// NOTE: It's divided in multiple controls:
// int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
// int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
@@ -3917,7 +4105,7 @@ int GuiMessageBox(Rectangle bounds, const char *title, const char *message, cons
buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount;
buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT;
- //int textWidth = GetTextWidth(message) + 2;
+ //int textWidth = GuiGetTextWidth(message) + 2;
Rectangle textBounds = { 0 };
textBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
@@ -3981,7 +4169,7 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co
Rectangle textBounds = { 0 };
if (message != NULL)
{
- int textSize = GetTextWidth(message) + 2;
+ int textSize = GuiGetTextWidth(message) + 2;
textBounds.x = bounds.x + bounds.width/2 - textSize/2;
textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + messageInputHeight/4 - (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -4221,7 +4409,7 @@ void GuiLoadStyle(const char *fileName)
if (fileDataSize > 0)
{
- unsigned char *fileData = (unsigned char *)RAYGUI_MALLOC(fileDataSize*sizeof(unsigned char));
+ unsigned char *fileData = (unsigned char *)RAYGUI_CALLOC(fileDataSize, sizeof(unsigned char));
fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile);
GuiLoadStyleFromMemory(fileData, fileDataSize);
@@ -4283,8 +4471,6 @@ void GuiLoadStyleDefault(void)
GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
GuiSetStyle(VALUEBOX, TEXT_PADDING, 0);
GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
- GuiSetStyle(SPINNER, TEXT_PADDING, 0);
- GuiSetStyle(SPINNER, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
GuiSetStyle(STATUSBAR, TEXT_PADDING, 8);
GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
@@ -4299,8 +4485,8 @@ void GuiLoadStyleDefault(void)
GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2);
GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16);
GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2);
- GuiSetStyle(SPINNER, SPIN_BUTTON_WIDTH, 24);
- GuiSetStyle(SPINNER, SPIN_BUTTON_SPACING, 2);
+ GuiSetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH, 24);
+ GuiSetStyle(VALUEBOX, SPINNER_BUTTON_SPACING, 2);
GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0);
GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0);
GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6);
@@ -4310,6 +4496,7 @@ void GuiLoadStyleDefault(void)
GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 12);
GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 28);
GuiSetStyle(LISTVIEW, LIST_ITEMS_SPACING, 2);
+ GuiSetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH, 1);
GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 12);
GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE);
GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 8);
@@ -4322,8 +4509,8 @@ void GuiLoadStyleDefault(void)
{
// Unload previous font texture
UnloadTexture(guiFont.texture);
- RL_FREE(guiFont.recs);
- RL_FREE(guiFont.glyphs);
+ RAYGUI_FREE(guiFont.recs);
+ RAYGUI_FREE(guiFont.glyphs);
guiFont.recs = NULL;
guiFont.glyphs = NULL;
@@ -4352,7 +4539,7 @@ const char *GuiIconText(int iconId, const char *text)
if (text != NULL)
{
memset(buffer, 0, 1024);
- sprintf(buffer, "#%03i#", iconId);
+ snprintf(buffer, 1024, "#%03i#", iconId);
for (int i = 5; i < 1024; i++)
{
@@ -4364,7 +4551,7 @@ const char *GuiIconText(int iconId, const char *text)
}
else
{
- sprintf(iconBuffer, "#%03i#", iconId);
+ snprintf(iconBuffer, 16, "#%03i#", iconId);
return iconBuffer;
}
@@ -4430,17 +4617,17 @@ char **GuiLoadIcons(const char *fileName, bool loadIconsName)
{
if (loadIconsName)
{
- guiIconsName = (char **)RAYGUI_MALLOC(iconCount*sizeof(char **));
+ guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *));
for (int i = 0; i < iconCount; i++)
{
- guiIconsName[i] = (char *)RAYGUI_MALLOC(RAYGUI_ICON_MAX_NAME_LENGTH);
+ guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char));
fread(guiIconsName[i], 1, RAYGUI_ICON_MAX_NAME_LENGTH, rgiFile);
}
}
else fseek(rgiFile, iconCount*RAYGUI_ICON_MAX_NAME_LENGTH, SEEK_CUR);
// Read icons data directly over internal icons array
- fread(guiIconsPtr, sizeof(unsigned int), iconCount*(iconSize*iconSize/32), rgiFile);
+ fread(guiIconsPtr, sizeof(unsigned int), (int)iconCount*((int)iconSize*(int)iconSize/32), rgiFile);
}
fclose(rgiFile);
@@ -4449,6 +4636,56 @@ char **GuiLoadIcons(const char *fileName, bool loadIconsName)
return guiIconsName;
}
+// Load icons from memory
+// WARNING: Binary files only
+char **GuiLoadIconsFromMemory(const unsigned char *fileData, int dataSize, bool loadIconsName)
+{
+ unsigned char *fileDataPtr = (unsigned char *)fileData;
+ char **guiIconsName = NULL;
+
+ char signature[5] = { 0 };
+ short version = 0;
+ short reserved = 0;
+ short iconCount = 0;
+ short iconSize = 0;
+
+ memcpy(signature, fileDataPtr, 4);
+ memcpy(&version, fileDataPtr + 4, sizeof(short));
+ memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short));
+ memcpy(&iconCount, fileDataPtr + 4 + 2 + 2, sizeof(short));
+ memcpy(&iconSize, fileDataPtr + 4 + 2 + 2 + 2, sizeof(short));
+ fileDataPtr += 12;
+
+ if ((signature[0] == 'r') &&
+ (signature[1] == 'G') &&
+ (signature[2] == 'I') &&
+ (signature[3] == ' '))
+ {
+ if (loadIconsName)
+ {
+ guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *));
+ for (int i = 0; i < iconCount; i++)
+ {
+ guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char));
+ memcpy(guiIconsName[i], fileDataPtr, RAYGUI_ICON_MAX_NAME_LENGTH);
+ fileDataPtr += RAYGUI_ICON_MAX_NAME_LENGTH;
+ }
+ }
+ else
+ {
+ // Skip icon name data if not required
+ fileDataPtr += iconCount*RAYGUI_ICON_MAX_NAME_LENGTH;
+ }
+
+ int iconDataSize = iconCount*((int)iconSize*(int)iconSize/32)*(int)sizeof(unsigned int);
+ guiIconsPtr = (unsigned int *)RAYGUI_CALLOC(iconDataSize, 1);
+
+ memcpy(guiIconsPtr, fileDataPtr, iconDataSize);
+ }
+
+ return guiIconsName;
+}
+
// Draw selected icon using rectangles pixel-by-pixel
void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color)
{
@@ -4476,12 +4713,73 @@ void GuiSetIconScale(int scale)
if (scale >= 1) guiIconScale = scale;
}
+// Get text width considering gui style and icon size (if required)
+int GuiGetTextWidth(const char *text)
+{
+ #if !defined(ICON_TEXT_PADDING)
+ #define ICON_TEXT_PADDING 4
+ #endif
+
+ Vector2 textSize = { 0 };
+ int textIconOffset = 0;
+
+ if ((text != NULL) && (text[0] != '\0'))
+ {
+ if (text[0] == '#')
+ {
+ for (int i = 1; (i < 5) && (text[i] != '\0'); i++)
+ {
+ if (text[i] == '#')
+ {
+ textIconOffset = i;
+ break;
+ }
+ }
+ }
+
+ text += textIconOffset;
+
+ // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly
+ float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+
+ // Custom MeasureText() implementation
+ if ((guiFont.texture.id > 0) && (text != NULL))
+ {
+ // Get size in bytes of text, considering end of line and line break
+ int size = 0;
+ for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++)
+ {
+ if ((text[i] != '\0') && (text[i] != '\n')) size++;
+ else break;
+ }
+
+ float scaleFactor = fontSize/(float)guiFont.baseSize;
+ textSize.y = (float)guiFont.baseSize*scaleFactor;
+ float glyphWidth = 0.0f;
+
+ for (int i = 0, codepointSize = 0; i < size; i += codepointSize)
+ {
+ int codepoint = GetCodepointNext(&text[i], &codepointSize);
+ int codepointIndex = GetGlyphIndex(guiFont, codepoint);
+
+ if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
+ else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
+
+ textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+ }
+ }
+
+ if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING);
+ }
+
+ return (int)textSize.x;
+}
+
#endif // !RAYGUI_NO_ICONS
//----------------------------------------------------------------------------------
// Module Internal Functions Definition
//----------------------------------------------------------------------------------
-
// Load style from memory
// WARNING: Binary files only
static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
@@ -4567,7 +4865,7 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
{
// Compressed font atlas image data (DEFLATE), it requires DecompressData()
int dataUncompSize = 0;
- unsigned char *compData = (unsigned char *)RAYGUI_MALLOC(fontImageCompSize);
+ unsigned char *compData = (unsigned char *)RAYGUI_CALLOC(fontImageCompSize, sizeof(unsigned char));
memcpy(compData, fileDataPtr, fontImageCompSize);
fileDataPtr += fontImageCompSize;
@@ -4581,7 +4879,7 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
else
{
// Font atlas image data is not compressed
- imFont.data = (unsigned char *)RAYGUI_MALLOC(fontImageUncompSize);
+ imFont.data = (unsigned char *)RAYGUI_CALLOC(fontImageUncompSize, sizeof(unsigned char));
memcpy(imFont.data, fileDataPtr, fontImageUncompSize);
fileDataPtr += fontImageUncompSize;
}
@@ -4609,7 +4907,7 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
if ((recsDataCompressedSize > 0) && (recsDataCompressedSize != recsDataSize))
{
// Recs data is compressed, uncompress it
- unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_MALLOC(recsDataCompressedSize);
+ unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_CALLOC(recsDataCompressedSize, sizeof(unsigned char));
memcpy(recsDataCompressed, fileDataPtr, recsDataCompressedSize);
fileDataPtr += recsDataCompressedSize;
@@ -4651,7 +4949,7 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
if ((glyphsDataCompressedSize > 0) && (glyphsDataCompressedSize != glyphsDataSize))
{
// Glyphs data is compressed, uncompress it
- unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_MALLOC(glyphsDataCompressedSize);
+ unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_CALLOC(glyphsDataCompressedSize, sizeof(unsigned char));
memcpy(glypsDataCompressed, fileDataPtr, glyphsDataCompressedSize);
fileDataPtr += glyphsDataCompressedSize;
@@ -4704,68 +5002,6 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
}
}
-// Gui get text width considering icon
-static int GetTextWidth(const char *text)
-{
- #if !defined(ICON_TEXT_PADDING)
- #define ICON_TEXT_PADDING 4
- #endif
-
- Vector2 textSize = { 0 };
- int textIconOffset = 0;
-
- if ((text != NULL) && (text[0] != '\0'))
- {
- if (text[0] == '#')
- {
- for (int i = 1; (i < 5) && (text[i] != '\0'); i++)
- {
- if (text[i] == '#')
- {
- textIconOffset = i;
- break;
- }
- }
- }
-
- text += textIconOffset;
-
- // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly
- float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
-
- // Custom MeasureText() implementation
- if ((guiFont.texture.id > 0) && (text != NULL))
- {
- // Get size in bytes of text, considering end of line and line break
- int size = 0;
- for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++)
- {
- if ((text[i] != '\0') && (text[i] != '\n')) size++;
- else break;
- }
-
- float scaleFactor = fontSize/(float)guiFont.baseSize;
- textSize.y = (float)guiFont.baseSize*scaleFactor;
- float glyphWidth = 0.0f;
-
- for (int i = 0, codepointSize = 0; i < size; i += codepointSize)
- {
- int codepoint = GetCodepointNext(&text[i], &codepointSize);
- int codepointIndex = GetGlyphIndex(guiFont, codepoint);
-
- if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
- else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
-
- textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
- }
- }
-
- if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING);
- }
-
- return (int)textSize.x;
-}
-
// Get text bounds considering control bounds
static Rectangle GetTextBounds(int control, Rectangle bounds)
{
@@ -4786,7 +5022,7 @@ static Rectangle GetTextBounds(int control, Rectangle bounds)
case SLIDER:
case CHECKBOX:
case VALUEBOX:
- case SPINNER:
+ case CONTROL11:
// TODO: More special cases (label on side): SLIDER, CHECKBOX, VALUEBOX, SPINNER
default:
{
@@ -4832,7 +5068,8 @@ static const char *GetTextIcon(const char *text, int *iconId)
}
// Get text divided into lines (by line-breaks '\n')
-const char **GetTextLines(const char *text, int *count)
+// WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator!
+static const char **GetTextLines(const char *text, int *count)
{
#define RAYGUI_MAX_TEXT_LINES 128
@@ -4842,25 +5079,18 @@ const char **GetTextLines(const char *text, int *count)
int textSize = (int)strlen(text);
lines[0] = text;
- int len = 0;
*count = 1;
- //int lineSize = 0; // Stores current line size, not returned
for (int i = 0, k = 0; (i < textSize) && (*count < RAYGUI_MAX_TEXT_LINES); i++)
{
if (text[i] == '\n')
{
- //lineSize = len;
k++;
- lines[k] = &text[i + 1]; // WARNING: next value is valid?
- len = 0;
+ lines[k] = &text[i + 1]; // WARNING: next value is valid?
*count += 1;
}
- else len++;
}
- //lines[*count - 1].size = len;
-
return lines;
}
@@ -4936,8 +5166,8 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C
float textBoundsWidthOffset = 0.0f;
// NOTE: We get text size after icon has been processed
- // WARNING: GetTextWidth() also processes text icon to get width! -> Really needed?
- int textSizeX = GetTextWidth(lines[i]);
+ // WARNING: GuiGetTextWidth() also processes text icon to get width! -> Really needed?
+ int textSizeX = GuiGetTextWidth(lines[i]);
// If text requires an icon, add size to measure
if (iconId >= 0)
@@ -5000,7 +5230,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C
float textOffsetX = 0.0f;
float glyphWidth = 0;
- int ellipsisWidth = GetTextWidth("...");
+ int ellipsisWidth = GuiGetTextWidth("...");
bool textOverflow = false;
for (int c = 0, codepointSize = 0; c < lineSize; c += codepointSize)
{
@@ -5144,13 +5374,13 @@ static void GuiTooltip(Rectangle controlRec)
if ((controlRec.x + textSize.x + 16) > GetScreenWidth()) controlRec.x -= (textSize.x + 16 - controlRec.width);
- GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.f }, NULL);
+ GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, NULL);
int textPadding = GuiGetStyle(LABEL, TEXT_PADDING);
int textAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
GuiSetStyle(LABEL, TEXT_PADDING, 0);
GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
- GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.f }, guiTooltipPtr);
+ GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, guiTooltipPtr);
GuiSetStyle(LABEL, TEXT_ALIGNMENT, textAlignment);
GuiSetStyle(LABEL, TEXT_PADDING, textPadding);
}
@@ -5204,7 +5434,7 @@ static const char **GuiTextSplit(const char *text, char delimiter, int *count, i
buffer[i] = '\0'; // Set an end of string at this point
counter++;
- if (counter > RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
+ if (counter >= RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
}
}
@@ -5526,10 +5756,10 @@ static Color GetColor(int hexValue)
{
Color color;
- color.r = (unsigned char)(hexValue >> 24) & 0xFF;
- color.g = (unsigned char)(hexValue >> 16) & 0xFF;
- color.b = (unsigned char)(hexValue >> 8) & 0xFF;
- color.a = (unsigned char)hexValue & 0xFF;
+ color.r = (unsigned char)(hexValue >> 24) & 0xff;
+ color.g = (unsigned char)(hexValue >> 16) & 0xff;
+ color.b = (unsigned char)(hexValue >> 8) & 0xff;
+ color.a = (unsigned char)hexValue & 0xff;
return color;
}
@@ -5562,7 +5792,7 @@ static const char *TextFormat(const char *text, ...)
va_list args;
va_start(args, text);
- vsprintf(buffer, text, args);
+ vsnprintf(buffer, RAYGUI_TEXTFORMAT_MAX_SIZE, text, args);
va_end(args);
return buffer;
@@ -5731,7 +5961,7 @@ static int GetCodepointNext(const char *text, int *codepointSize)
}
else if (0xe0 == (0xf0 & ptr[0]))
{
- // 3 byte UTF-8 codepoint */
+ // 3 byte UTF-8 codepoint
if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]);
*codepointSize = 3;
diff --git a/examples/examples_list.txt b/examples/examples_list.txt
index 7c8709785..3310cf2d2 100644
--- a/examples/examples_list.txt
+++ b/examples/examples_list.txt
@@ -25,7 +25,7 @@ core;core_3d_camera_mode;★☆☆☆;1.0;1.0;2014;2025;"Ramon Santamaria";@rays
core;core_3d_camera_free;★☆☆☆;1.3;1.3;2015;2025;"Ramon Santamaria";@raysan5
core;core_3d_camera_first_person;★★☆☆;1.3;1.3;2015;2025;"Ramon Santamaria";@raysan5
core;core_3d_camera_split_screen;★★★☆;3.7;4.0;2021;2025;"Jeffery Myers";@JeffM2501
-core;core_3d_camera_fps;★★★☆;5.5;5.5;2025;2025;"Agnis Aldins";@nezvers
+core;core_3d_camera_fps;★★★☆;5.5;5.5;2025;2025;"Agnis Aldiņš";@nezvers
core;core_3d_picking;★★☆☆;1.3;4.0;2015;2025;"Ramon Santamaria";@raysan5
core;core_world_screen;★★☆☆;1.3;1.4;2015;2025;"Ramon Santamaria";@raysan5
core;core_window_flags;★★★☆;3.5;3.5;2020;2025;"Ramon Santamaria";@raysan5
@@ -43,15 +43,17 @@ core;core_custom_frame_control;★★★★;4.0;4.0;2021;2025;"Ramon Santamaria"
core;core_smooth_pixelperfect;★★★☆;3.7;4.0;2021;2025;"Giancamillo Alessandroni";@NotManyIdeasDev
core;core_random_sequence;★☆☆☆;5.0;5.0;2023;2025;"Dalton Overmyer";@REDl3east
core;core_automation_events;★★★☆;5.0;5.0;2023;2025;"Ramon Santamaria";@raysan5
-core;core_high_dpi;★★☆☆;5.0;5.5;2025;2025;"Jonathan Marler";@marler8997
+core;core_highdpi_demo;★★☆☆;5.0;5.5;2025;2025;"Jonathan Marler";@marler8997
core;core_render_texture;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"Ramon Santamaria";@raysan5
core;core_undo_redo;★★★☆;5.5;5.6;2025;2025;"Ramon Santamaria";@raysan5
+core;core_viewport_scaling;★★☆☆;5.5;5.5;2025;2025;"Agnis Aldiņš";@nezvers
core;core_input_actions;★★☆☆;5.5;5.6;2025;2025;"Jett";@JettMonstersGoBoom
core;core_directory_files;★☆☆☆;5.5;5.6;2025;2025;"Hugo ARNAL";@hugoarnal
core;core_highdpi_testbed;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"Ramon Santamaria";@raysan5
-core;core_screen_recording;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"Ramon Santamaria";@raysan5
-core;core_clipboard_text;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"Robin";@RobinsAviary
+core;core_screen_recording;★★☆☆;5.6-dev;5.6-dev;2025;2025;"Ramon Santamaria";@raysan5
+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
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
@@ -86,6 +88,10 @@ shapes;shapes_simple_particles;★★☆☆;5.6;5.6;2025;2025;"Jordi Santonja";@
shapes;shapes_starfield_effect;★★☆☆;5.5;5.6-dev;2025;2025;"JP Mortiboys";@themushroompirates
shapes;shapes_lines_drawing;★☆☆☆;5.6-dev;5.6;2025;2025;"Robin";@RobinsAviary
shapes;shapes_math_angle_rotation;★☆☆☆;5.6-dev;5.6;2025;2025;"Kris";@krispy-snacc
+shapes;shapes_rlgl_color_wheel;★★★☆;5.6-dev;5.6-dev;2025;2025;"Robin";@RobinsAviary
+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
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
@@ -111,7 +117,10 @@ textures;textures_gif_player;★★★☆;4.2;4.2;2021;2025;"Ramon Santamaria";@
textures;textures_image_kernel;★★★★;1.3;1.3;2015;2025;"Karim Salem";@kimo-s
textures;textures_image_channel;★★☆☆;5.5;5.5;2024;2025;"Bruno Cabral";@brccabral
textures;textures_image_rotate;★★☆☆;1.0;1.0;2014;2025;"Ramon Santamaria";@raysan5
+textures;textures_screen_buffer;★★☆☆;5.5;5.5;2025;2025;"Agnis Aldiņš";@nezvers
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
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
@@ -127,6 +136,7 @@ text;text_3d_drawing;★★★★;3.5;4.0;2021;2025;"Vlad Adrian";@demizdor
text;text_codepoints_loading;★★★☆;4.2;4.2;2022;2025;"Ramon Santamaria";@raysan5
text;text_inline_styling;★★★☆;5.6-dev;5.6-dev;2025;2025;"Wagner Barongello";@SultansOfCode
text;text_words_alignment;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"JP Mortiboys";@themushroompirates
+text;text_strings_management;★★★☆;5.6-dev;5.6-dev;2025;2025;"David Buzatto";@davidbuzatto
models;models_animation_playing;★★☆☆;2.5;3.5;2019;2025;"Culacant";@culacant
models;models_billboard_rendering;★★★☆;1.3;3.5;2015;2025;"Ramon Santamaria";@raysan5
models;models_box_collisions;★☆☆☆;1.3;3.5;2015;2025;"Ramon Santamaria";@raysan5
@@ -153,6 +163,7 @@ models;models_tesseract_view;★★☆☆;5.6-dev;5.6-dev;2024;2025;"Timothy van
models;models_basic_voxel;★★☆☆;5.5;5.5;2025;2025;"Tim Little";@timlittle
models;models_rotating_cube;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"Jopestpe";@jopestpe
models;models_decals;★★★★;5.6-dev;5.6-dev;2025;2025;"JP Mortiboys";@themushroompirates
+models;models_directional_billboard;★★☆☆;5.6-dev;5.6;2025;2025;"Robin";@RobinsAviary
shaders;shaders_ascii_rendering;★★☆☆;5.5;5.6;2025;2025;"Maicon Santana";@maiconpintoabreu
shaders;shaders_basic_lighting;★★★★;3.0;4.2;2019;2025;"Chris Camacho";@chriscamacho
shaders;shaders_model_shader;★★☆☆;1.3;3.7;2014;2025;"Ramon Santamaria";@raysan5
@@ -185,6 +196,7 @@ shaders;shaders_basic_pbr;★★★★;5.0;5.5;2023;2025;"Afan OLOVCIC";@_DevDad
shaders;shaders_lightmap_rendering;★★★☆;4.5;4.5;2019;2025;"Jussi Viitala";@nullstare
shaders;shaders_rounded_rectangle;★★★☆;5.5;5.5;2025;2025;"Anstro Pleuton";@anstropleuton
shaders;shaders_depth_rendering;★★★☆;5.6-dev;5.6-dev;2025;2025;"Luís Almeida";@luis605
+shaders;shaders_game_of_life;★★★☆;5.6;5.6;2025;2025;"Jordi Santonja";@JordSant
audio;audio_module_playing;★☆☆☆;1.5;3.5;2016;2025;"Ramon Santamaria";@raysan5
audio;audio_music_stream;★☆☆☆;1.3;4.2;2015;2025;"Ramon Santamaria";@raysan5
audio;audio_raw_stream;★★★☆;1.6;4.2;2015;2025;"Ramon Santamaria";@raysan5
@@ -193,6 +205,7 @@ audio;audio_mixed_processor;★★★★;4.2;4.2;2023;2025;"hkc";@hatkidchan
audio;audio_stream_effects;★★★★;4.2;5.0;2022;2025;"Ramon Santamaria";@raysan5
audio;audio_sound_multi;★★☆☆;5.0;5.0;2023;2025;"Jeffery Myers";@JeffM2501
audio;audio_sound_positioning;★★☆☆;5.5;5.5;2025;2025;"Le Juez Victor";@Bigfoot71
+audio;audio_spectrum_visualizer;★★★☆;6.0;5.6-dev;2025;2025;"IANN";@meisei4
others;rlgl_standalone;★★★★;1.6;4.0;2014;2025;"Ramon Santamaria";@raysan5
others;rlgl_compute_shader;★★★★;4.0;4.0;2021;2025;"Teddy Astie";@tsnake41
others;easings_testbed;★★★☆;2.5;3.0;2019;2025;"Juan Miguel López";@flashback-fx
diff --git a/examples/models/models_directional_billboard.c b/examples/models/models_directional_billboard.c
new file mode 100644
index 000000000..e2c75c15a
--- /dev/null
+++ b/examples/models/models_directional_billboard.c
@@ -0,0 +1,116 @@
+/*******************************************************************************************
+*
+* raylib [models] example - directional billboard
+*
+* Example complexity rating: [★★☆☆] 2/4
+*
+* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6
+*
+* Example contributed by Robin (@RobinsAviary) 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 Robin (@RobinsAviary)
+* Killbot art by patvanmackelberg https://opengameart.org/content/killbot-8-directional under CC0
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include "raymath.h"
+#include
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [models] example - directional billboard");
+
+ // Set up the camera
+ Camera camera = { 0 };
+ camera.position = (Vector3){ 2.0f, 1.0f, 2.0f }; // Starting position
+ camera.target = (Vector3){ 0.0f, 0.5f, 0.0f }; // Target position
+ camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Up vector
+ camera.fovy = 45.0f; // FOV
+ camera.projection = CAMERA_PERSPECTIVE; // Projection type (Standard 3D perspective)
+
+ // Load billboard texture
+ Texture skillbot = LoadTexture("resources/skillbot.png");
+
+ // Timer to update animation
+ float anim_timer = 0.0f;
+ // Animation frame
+ unsigned int anim = 0;
+
+ SetTargetFPS(60);
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ UpdateCamera(&camera, CAMERA_ORBITAL);
+
+ // Update timer with delta time
+ anim_timer += GetFrameTime();
+
+ // Update frame index after a certain amount of time (half a second)
+ if (anim_timer > 0.5f)
+ {
+ anim_timer = 0.0f;
+ anim += 1;
+ }
+
+ // Reset frame index to zero on overflow
+ if (anim >= 4) anim = 0;
+
+ // Find the current direction frame based on the camera position to the billboard object
+ float dir = (float)floor(((Vector2Angle((Vector2){ 2.0f, 0.0f }, (Vector2){ camera.position.x, camera.position.z })/PI)*4.0f) + 0.25f);
+
+ // Correct frame index if angle is negative
+ if (dir < 0.0f)
+ {
+ dir = 8.0f - (float)abs((int)dir);
+ }
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ BeginMode3D(camera);
+
+ DrawGrid(10, 1.0f);
+
+ // Draw billboard pointing straight up to the sky, rotated relative to the camera and offset from the bottom
+ DrawBillboardPro(camera, skillbot, (Rectangle){ 0.0f + (anim*24.0f), 0.0f + (dir*24.0f), 24.0f, 24.0f }, Vector3Zero(), (Vector3){ 0.0f, 1.0f, 0.0f }, Vector2One(), (Vector2){ 0.5f, 0.0f }, 0, WHITE);
+
+ EndMode3D();
+
+ // Render various variables for reference
+ DrawText(TextFormat("animation: %d", anim), 10, 10, 20, DARKGRAY);
+ DrawText(TextFormat("direction frame: %.0f", dir), 10, 40, 20, DARKGRAY);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ // Unload billboard texture
+ UnloadTexture(skillbot);
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
\ No newline at end of file
diff --git a/examples/models/models_directional_billboard.png b/examples/models/models_directional_billboard.png
new file mode 100644
index 000000000..cbbe11065
Binary files /dev/null and b/examples/models/models_directional_billboard.png differ
diff --git a/examples/models/models_loading_vox.c b/examples/models/models_loading_vox.c
index 956c181a0..06dc651d7 100644
--- a/examples/models/models_loading_vox.c
+++ b/examples/models/models_loading_vox.c
@@ -35,171 +35,157 @@
//------------------------------------------------------------------------------------
int main(void)
{
- // Initialization
- //--------------------------------------------------------------------------------------
- const int screenWidth = 800;
- const int screenHeight = 450;
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
- const char *voxFileNames[] = {
- "resources/models/vox/chr_knight.vox",
- "resources/models/vox/chr_sword.vox",
- "resources/models/vox/monu9.vox",
- "resources/models/vox/fez.vox"
- };
+ const char *voxFileNames[] = {
+ "resources/models/vox/chr_knight.vox",
+ "resources/models/vox/chr_sword.vox",
+ "resources/models/vox/monu9.vox",
+ "resources/models/vox/fez.vox"
+ };
- InitWindow(screenWidth, screenHeight, "raylib [models] example - loading vox");
+ InitWindow(screenWidth, screenHeight, "raylib [models] example - loading vox");
- // Define the camera to look into our 3d world
- Camera camera = { 0 };
- camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position
- camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point
- camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
- camera.fovy = 45.0f; // Camera field-of-view Y
- camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
+ // Define the camera to look into our 3d world
+ Camera camera = { 0 };
+ camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position
+ camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point
+ camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
+ camera.fovy = 45.0f; // Camera field-of-view Y
+ camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
- // Load MagicaVoxel files
- Model models[MAX_VOX_FILES] = { 0 };
+ // Load MagicaVoxel files
+ Model models[MAX_VOX_FILES] = { 0 };
- for (int i = 0; i < MAX_VOX_FILES; i++)
- {
- // Load VOX file and measure time
- double t0 = GetTime()*1000.0;
- models[i] = LoadModel(voxFileNames[i]);
- double t1 = GetTime()*1000.0;
+ for (int i = 0; i < MAX_VOX_FILES; i++)
+ {
+ // Load VOX file and measure time
+ double t0 = GetTime()*1000.0;
+ models[i] = LoadModel(voxFileNames[i]);
+ double t1 = GetTime()*1000.0;
- TraceLog(LOG_WARNING, TextFormat("[%s] File loaded in %.3f ms", voxFileNames[i], t1 - t0));
+ TraceLog(LOG_INFO, TextFormat("[%s] Model file loaded in %.3f ms", voxFileNames[i], t1 - t0));
- // Compute model translation matrix to center model on draw position (0, 0 , 0)
- BoundingBox bb = GetModelBoundingBox(models[i]);
- Vector3 center = { 0 };
- center.x = bb.min.x + (((bb.max.x - bb.min.x)/2));
- center.z = bb.min.z + (((bb.max.z - bb.min.z)/2));
+ // Compute model translation matrix to center model on draw position (0, 0 , 0)
+ BoundingBox bb = GetModelBoundingBox(models[i]);
+ Vector3 center = { 0 };
+ center.x = bb.min.x + (((bb.max.x - bb.min.x)/2));
+ center.z = bb.min.z + (((bb.max.z - bb.min.z)/2));
- Matrix matTranslate = MatrixTranslate(-center.x, 0, -center.z);
- models[i].transform = matTranslate;
- }
+ Matrix matTranslate = MatrixTranslate(-center.x, 0, -center.z);
+ models[i].transform = matTranslate;
+ }
- int currentModel = 0;
+ int currentModel = 0;
+ Vector3 modelpos = { 0 };
+ Vector3 camerarot = { 0 };
- // Load voxel shader
- Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/voxel_lighting.vs", GLSL_VERSION),
- TextFormat("resources/shaders/glsl%i/voxel_lighting.fs", GLSL_VERSION));
+ // Load voxel shader
+ Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/voxel_lighting.vs", GLSL_VERSION),
+ TextFormat("resources/shaders/glsl%i/voxel_lighting.fs", GLSL_VERSION));
- // Get some required shader locations
- shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos");
- // NOTE: "matModel" location name is automatically assigned on shader loading,
- // no need to get the location again if using that uniform name
- //shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel");
+ // Get some required shader locations
+ shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos");
+ // NOTE: "matModel" location name is automatically assigned on shader loading,
+ // no need to get the location again if using that uniform name
+ //shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel");
- // Ambient light level (some basic lighting)
- int ambientLoc = GetShaderLocation(shader, "ambient");
- SetShaderValue(shader, ambientLoc, (float[4]) { 0.1f, 0.1f, 0.1f, 1.0f }, SHADER_UNIFORM_VEC4);
+ // Ambient light level (some basic lighting)
+ int ambientLoc = GetShaderLocation(shader, "ambient");
+ SetShaderValue(shader, ambientLoc, (float[4]) { 0.1f, 0.1f, 0.1f, 1.0f }, SHADER_UNIFORM_VEC4);
- // Assign out lighting shader to model
- for (int i = 0; i < MAX_VOX_FILES; i++)
- {
- Model m = models[i];
- for (int j = 0; j < m.materialCount; j++)
- {
- m.materials[j].shader = shader;
- }
- }
+ // Assign out lighting shader to model
+ for (int i = 0; i < MAX_VOX_FILES; i++)
+ {
+ for (int j = 0; j < models[i].materialCount; j++) models[i].materials[j].shader = shader;
+ }
- // Create lights
- Light lights[MAX_LIGHTS] = { 0 };
- lights[0] = CreateLight(LIGHT_POINT, (Vector3) { -20, 20, -20 }, Vector3Zero(), GRAY, shader);
- lights[1] = CreateLight(LIGHT_POINT, (Vector3) { 20, -20, 20 }, Vector3Zero(), GRAY, shader);
- lights[2] = CreateLight(LIGHT_POINT, (Vector3) { -20, 20, 20 }, Vector3Zero(), GRAY, shader);
- lights[3] = CreateLight(LIGHT_POINT, (Vector3) { 20, -20, -20 }, Vector3Zero(), GRAY, shader);
+ // Create lights
+ Light lights[MAX_LIGHTS] = { 0 };
+ lights[0] = CreateLight(LIGHT_POINT, (Vector3) { -20, 20, -20 }, Vector3Zero(), GRAY, shader);
+ lights[1] = CreateLight(LIGHT_POINT, (Vector3) { 20, -20, 20 }, Vector3Zero(), GRAY, shader);
+ lights[2] = CreateLight(LIGHT_POINT, (Vector3) { -20, 20, 20 }, Vector3Zero(), GRAY, shader);
+ lights[3] = CreateLight(LIGHT_POINT, (Vector3) { 20, -20, -20 }, Vector3Zero(), GRAY, shader);
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second
+ //--------------------------------------------------------------------------------------
- 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
+ //----------------------------------------------------------------------------------
+ if (IsMouseButtonDown(MOUSE_BUTTON_MIDDLE))
+ {
+ const Vector2 mouseDelta = GetMouseDelta();
+ camerarot.x = mouseDelta.x*0.05f;
+ camerarot.y = mouseDelta.y*0.05f;
+ }
+ else
+ {
+ camerarot.x = 0;
+ camerarot.y = 0;
+ }
- //--------------------------------------------------------------------------------------
- Vector3 modelpos = { 0 };
- Vector3 camerarot = { 0 };
+ UpdateCameraPro(&camera,
+ (Vector3){ (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))*0.1f - (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))*0.1f, // Move forward-backward
+ (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))*0.1f - (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))*0.1f, // Move right-left
+ 0.0f }, // Move up-down
+ camerarot, // Camera rotation
+ GetMouseWheelMove()*-2.0f); // Move to target (zoom)
- // Main game loop
- while (!WindowShouldClose()) // Detect window close button or ESC key
- {
- // Update
- //----------------------------------------------------------------------------------
- if (IsMouseButtonDown(MOUSE_BUTTON_MIDDLE))
- {
- const Vector2 mouseDelta = GetMouseDelta();
- camerarot.x = mouseDelta.x*0.05f;
- camerarot.y = mouseDelta.y*0.05f;
- }
- else
- {
- camerarot.x = 0;
- camerarot.y = 0;
- }
+ // Cycle between models on mouse click
+ if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) currentModel = (currentModel + 1)%MAX_VOX_FILES;
- UpdateCameraPro(&camera,
- (Vector3) {
- (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))*0.1f - // Move forward-backward
- (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))*0.1f,
- (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))*0.1f - // Move right-left
- (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))*0.1f,
- 0.0f // Move up-down
- },
- camerarot,
- GetMouseWheelMove()*-2.0f); // Move to target (zoom)
+ // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
+ float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z };
+ SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3);
- // Cycle between models on mouse click
- if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) currentModel = (currentModel + 1) % MAX_VOX_FILES;
+ // Update light values (actually, only enable/disable them)
+ for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(shader, lights[i]);
+ //----------------------------------------------------------------------------------
- // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
- float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z };
- SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3);
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
- // Update light values (actually, only enable/disable them)
- for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(shader, lights[i]);
+ ClearBackground(RAYWHITE);
- //----------------------------------------------------------------------------------
- // Draw
- //----------------------------------------------------------------------------------
- BeginDrawing();
+ // Draw 3D model
+ BeginMode3D(camera);
+ DrawModel(models[currentModel], modelpos, 1.0f, WHITE);
+ DrawGrid(10, 1.0);
- ClearBackground(RAYWHITE);
+ // Draw spheres to show where the lights are
+ for (int i = 0; i < MAX_LIGHTS; i++)
+ {
+ if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lights[i].color);
+ else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(lights[i].color, 0.3f));
+ }
+ EndMode3D();
- // Draw 3D model
- BeginMode3D(camera);
+ // Display info
+ DrawRectangle(10, 40, 340, 70, Fade(SKYBLUE, 0.5f));
+ DrawRectangleLines(10, 40, 340, 70, Fade(DARKBLUE, 0.5f));
+ DrawText("- MOUSE LEFT BUTTON: CYCLE VOX MODELS", 20, 50, 10, BLUE);
+ DrawText("- MOUSE MIDDLE BUTTON: ZOOM OR ROTATE CAMERA", 20, 70, 10, BLUE);
+ DrawText("- UP-DOWN-LEFT-RIGHT KEYS: MOVE CAMERA", 20, 90, 10, BLUE);
+ DrawText(TextFormat("Model file: %s", GetFileName(voxFileNames[currentModel])), 10, 10, 20, GRAY);
- DrawModel(models[currentModel], modelpos, 1.0f, WHITE);
- DrawGrid(10, 1.0);
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
- // Draw spheres to show where the lights are
- for (int i = 0; i < MAX_LIGHTS; i++)
- {
- if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lights[i].color);
- else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(lights[i].color, 0.3f));
- }
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ // Unload models data (GPU VRAM)
+ for (int i = 0; i < MAX_VOX_FILES; i++) UnloadModel(models[i]);
- EndMode3D();
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
- // Display info
- DrawRectangle(10, 400, 340, 60, Fade(SKYBLUE, 0.5f));
- DrawRectangleLines(10, 400, 340, 60, Fade(DARKBLUE, 0.5f));
- DrawText("MOUSE LEFT BUTTON to CYCLE VOX MODELS", 40, 410, 10, BLUE);
- DrawText("MOUSE MIDDLE BUTTON to ZOOM OR ROTATE CAMERA", 40, 420, 10, BLUE);
- DrawText("UP-DOWN-LEFT-RIGHT KEYS to MOVE CAMERA", 40, 430, 10, BLUE);
- DrawText(TextFormat("File: %s", GetFileName(voxFileNames[currentModel])), 10, 10, 20, GRAY);
-
- EndDrawing();
- //----------------------------------------------------------------------------------
- }
-
- // De-Initialization
- //--------------------------------------------------------------------------------------
- // Unload models data (GPU VRAM)
- for (int i = 0; i < MAX_VOX_FILES; i++) UnloadModel(models[i]);
-
- CloseWindow(); // Close window and OpenGL context
- //--------------------------------------------------------------------------------------
-
- return 0;
+ return 0;
}
-
-
diff --git a/examples/models/models_point_rendering.c b/examples/models/models_point_rendering.c
index 3a93fc8d9..71b907225 100644
--- a/examples/models/models_point_rendering.c
+++ b/examples/models/models_point_rendering.c
@@ -32,7 +32,7 @@ static Mesh GenMeshPoints(int numPoints);
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
-int main()
+int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
@@ -57,7 +57,7 @@ int main()
Mesh mesh = GenMeshPoints(numPoints);
Model model = LoadModelFromMesh(mesh);
- //SetTargetFPS(60);
+ SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
@@ -92,15 +92,12 @@ int main()
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
+
ClearBackground(BLACK);
BeginMode3D(camera);
-
// The new method only uploads the points once to the GPU
- if (useDrawModelPoints)
- {
- DrawModelPoints(model, position, 1.0f, WHITE);
- }
+ if (useDrawModelPoints) DrawModelPoints(model, position, 1.0f, WHITE);
else
{
// The old method must continually draw the "points" (lines)
@@ -124,17 +121,16 @@ int main()
// Draw a unit sphere for reference
DrawSphereWires(position, 1.0f, 10, 10, YELLOW);
-
EndMode3D();
// Draw UI text
- DrawText(TextFormat("Point Count: %d", numPoints), 20, screenHeight - 50, 40, WHITE);
- DrawText("Up - increase points", 20, 70, 20, WHITE);
- DrawText("Down - decrease points", 20, 100, 20, WHITE);
- DrawText("Space - drawing function", 20, 130, 20, WHITE);
+ DrawText(TextFormat("Point Count: %d", numPoints), 10, screenHeight - 50, 40, WHITE);
+ DrawText("UP - Increase points", 10, 40, 20, WHITE);
+ DrawText("DOWN - Decrease points", 10, 70, 20, WHITE);
+ DrawText("SPACE - Drawing function", 10, 100, 20, WHITE);
- if (useDrawModelPoints) DrawText("Using: DrawModelPoints()", 20, 160, 20, GREEN);
- else DrawText("Using: DrawPoint3D()", 20, 160, 20, RED);
+ if (useDrawModelPoints) DrawText("Using: DrawModelPoints()", 10, 130, 20, GREEN);
+ else DrawText("Using: DrawPoint3D()", 10, 130, 20, RED);
DrawFPS(10, 10);
diff --git a/examples/models/models_rotating_cube.c b/examples/models/models_rotating_cube.c
index 930dae516..c5b633fea 100644
--- a/examples/models/models_rotating_cube.c
+++ b/examples/models/models_rotating_cube.c
@@ -5,7 +5,7 @@
* Example complexity rating: [★☆☆☆] 1/4
*
* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
-*
+*
* Example contributed by Jopestpe (@jopestpe)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
@@ -48,7 +48,7 @@ int main(void)
model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture;
float rotation = 0.0f;
-
+
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
@@ -59,7 +59,7 @@ int main(void)
//----------------------------------------------------------------------------------
rotation += 1.0f;
//----------------------------------------------------------------------------------
-
+
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
@@ -67,13 +67,13 @@ int main(void)
ClearBackground(RAYWHITE);
BeginMode3D(camera);
-
- // Draw model defining: position, size, rotation-axis, rotation (degrees), size, and tint-color
- DrawModelEx(model, (Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.5f, 1.0f, 0.0f },
+
+ // Draw model defining: position, size, rotation-axis, rotation (degrees), size, and tint-color
+ DrawModelEx(model, (Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.5f, 1.0f, 0.0f },
rotation, (Vector3){ 1.0f, 1.0f, 1.0f }, WHITE);
-
+
DrawGrid(10, 1.0f);
-
+
EndMode3D();
DrawFPS(10, 10);
diff --git a/examples/models/models_skybox_rendering.c b/examples/models/models_skybox_rendering.c
index ddce6a28f..9359e03dc 100644
--- a/examples/models/models_skybox_rendering.c
+++ b/examples/models/models_skybox_rendering.c
@@ -54,7 +54,8 @@ int main(void)
Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f);
Model skybox = LoadModelFromMesh(cube);
- // Set this to true to use an HDR Texture, Note that raylib must be built with HDR Support for this to work SUPPORT_FILEFORMAT_HDR
+ // Set this to true to use an HDR Texture
+ // NOTE: raylib must be built with HDR Support for this to work: SUPPORT_FILEFORMAT_HDR
bool useHDR = false;
// Load skybox shader and set required locations
@@ -63,8 +64,8 @@ int main(void)
TextFormat("resources/shaders/glsl%i/skybox.fs", GLSL_VERSION));
SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "environmentMap"), (int[1]){ MATERIAL_MAP_CUBEMAP }, SHADER_UNIFORM_INT);
- SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "doGamma"), (int[1]) { useHDR ? 1 : 0 }, SHADER_UNIFORM_INT);
- SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "vflipped"), (int[1]){ useHDR ? 1 : 0 }, SHADER_UNIFORM_INT);
+ SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "doGamma"), (int[1]){ useHDR? 1 : 0 }, SHADER_UNIFORM_INT);
+ SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "vflipped"), (int[1]){ useHDR? 1 : 0 }, SHADER_UNIFORM_INT);
// Load cubemap shader and setup required shader locations
Shader shdrCubemap = LoadShader(TextFormat("resources/shaders/glsl%i/cubemap.vs", GLSL_VERSION),
@@ -91,9 +92,11 @@ int main(void)
}
else
{
- Image img = LoadImage("resources/skybox.png");
- skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = LoadTextureCubemap(img, CUBEMAP_LAYOUT_AUTO_DETECT); // CUBEMAP_LAYOUT_PANORAMA
- UnloadImage(img);
+ // TODO: WARNING: On PLATFORM_WEB it requires a big amount of memory to process input image
+ // and generate the required cubemap image to be passed to rlLoadTextureCubemap()
+ Image image = LoadImage("resources/skybox.png");
+ skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = LoadTextureCubemap(image, CUBEMAP_LAYOUT_AUTO_DETECT);
+ UnloadImage(image);
}
DisableCursor(); // Limit cursor to relative movement inside the window
@@ -132,9 +135,9 @@ int main(void)
}
else
{
- Image img = LoadImage(droppedFiles.paths[0]);
- skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = LoadTextureCubemap(img, CUBEMAP_LAYOUT_AUTO_DETECT);
- UnloadImage(img);
+ Image image = LoadImage(droppedFiles.paths[0]);
+ skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = LoadTextureCubemap(image, CUBEMAP_LAYOUT_AUTO_DETECT);
+ UnloadImage(image);
}
TextCopy(skyboxFileName, droppedFiles.paths[0]);
diff --git a/examples/models/models_waving_cubes.c b/examples/models/models_waving_cubes.c
index 51febf39d..7996c1c8a 100644
--- a/examples/models/models_waving_cubes.c
+++ b/examples/models/models_waving_cubes.c
@@ -22,7 +22,7 @@
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
-int main()
+int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
diff --git a/examples/models/resources/skillbot.png b/examples/models/resources/skillbot.png
new file mode 100644
index 000000000..537f13383
Binary files /dev/null and b/examples/models/resources/skillbot.png differ
diff --git a/examples/others/easings_testbed.c b/examples/others/easings_testbed.c
index b3ff8a8df..fa4a599ee 100644
--- a/examples/others/easings_testbed.c
+++ b/examples/others/easings_testbed.c
@@ -221,7 +221,7 @@ int main(void)
}
-// NoEase function, used when "no easing" is selected for any axis
+// NoEase function, used when "no easing" is selected for any axis
// It just ignores all parameters besides b
static float NoEase(float t, float b, float c, float d)
{
diff --git a/examples/others/web_basic_window.c b/examples/others/web_basic_window.c
index f85c6e4f6..217c47fbc 100644
--- a/examples/others/web_basic_window.c
+++ b/examples/others/web_basic_window.c
@@ -36,7 +36,7 @@ void UpdateDrawFrame(void); // Update and Draw one frame
//----------------------------------------------------------------------------------
// Program main entry point
//----------------------------------------------------------------------------------
-int main()
+int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
diff --git a/examples/shaders/raygui.h b/examples/shaders/raygui.h
index a3fc51f0f..88fe5cc5b 100644
--- a/examples/shaders/raygui.h
+++ b/examples/shaders/raygui.h
@@ -4,7 +4,7 @@
*
* DESCRIPTION:
* raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also
-* available as a standalone library, as long as input and drawing functions are provided.
+* available as a standalone library, as long as input and drawing functions are provided
*
* FEATURES:
* - Immediate-mode gui, minimal retained data
@@ -27,7 +27,7 @@
* - WARNING: GuiLoadStyle() and GuiLoadStyle{Custom}() functions, allocate memory for
* font atlas recs and glyphs, freeing that memory is (usually) up to the user,
* no unload function is explicitly provided... but note that GuiLoadStyleDefault() unloads
-* by default any previously loaded font (texture, recs, glyphs).
+* by default any previously loaded font (texture, recs, glyphs)
* - Global UI alpha (guiAlpha) is applied inside GuiDrawRectangle() and GuiDrawText() functions
*
* CONTROLS PROVIDED:
@@ -65,7 +65,7 @@
* - MessageBox --> Window, Label, Button
* - TextInputBox --> Window, Label, TextBox, Button
*
-* It also provides a set of functions for styling the controls based on its properties (size, color).
+* It also provides a set of functions for styling the controls based on its properties (size, color)
*
*
* RAYGUI STYLE (guiStyle):
@@ -77,11 +77,11 @@
*
* static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)];
*
-* guiStyle size is by default: 16*(16 + 8) = 384*4 = 1536 bytes = 1.5 KB
+* guiStyle size is by default: 16*(16 + 8) = 384 int = 384*4 bytes = 1536 bytes = 1.5 KB
*
* Note that the first set of BASE properties (by default guiStyle[0..15]) belong to the generic style
* used for all controls, when any of those base values is set, it is automatically populated to all
-* controls, so, specific control values overwriting generic style should be set after base values.
+* controls, so, specific control values overwriting generic style should be set after base values
*
* After the first BASE set we have the EXTENDED properties (by default guiStyle[16..23]), those
* properties are actually common to all controls and can not be overwritten individually (like BASE ones)
@@ -100,7 +100,7 @@
* Every icon is codified in binary form, using 1 bit per pixel, so, every 16x16 icon
* requires 8 integers (16*16/32) to be stored in memory.
*
-* When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set.
+* When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set
*
* The global icons array size is fixed and depends on the number of icons and size:
*
@@ -112,20 +112,20 @@
*
* RAYGUI LAYOUT:
* raygui currently does not provide an auto-layout mechanism like other libraries,
-* layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it.
+* layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it
*
* TOOL: rGuiLayout is a visual tool to create raygui layouts: github.com/raysan5/rguilayout
*
* CONFIGURATION:
* #define RAYGUI_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 RAYGUI_STANDALONE
* Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined
* internally in the library and input management and drawing functions must be provided by
-* the user (check library implementation for further details).
+* the user (check library implementation for further details)
*
* #define RAYGUI_NO_ICONS
* Avoid including embedded ricons data (256 icons, 16x16 pixels, 1-bit per pixel, 2KB)
@@ -141,12 +141,17 @@
* Draw text bounds rectangles for debug
*
* VERSIONS HISTORY:
-* 4.5-dev (Sep-2024) Current dev version...
+* 5.0 (xx-Nov-2025) ADDED: Support up to 32 controls (v500)
* ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes
* ADDED: GuiValueBoxFloat()
* ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP
* ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH
+* ADDED: GuiLoadIconsFromMemory()
* ADDED: Multiple new icons
+* REMOVED: GuiSpinner() from controls list, using BUTTON + VALUEBOX properties
+* REMOVED: GuiSliderPro(), functionality was redundant
+* REVIEWED: Controls using text labels to use LABEL properties
+* REVIEWED: Replaced sprintf() by snprintf() for more safety
* REVIEWED: GuiTabBar(), close tab with mouse middle button
* REVIEWED: GuiScrollPanel(), scroll speed proportional to content
* REVIEWED: GuiDropdownBox(), support roll up and hidden arrow
@@ -156,6 +161,8 @@
* REVIEWED: GuiIconText(), increase buffer size and reviewed padding
* REVIEWED: GuiDrawText(), improved wrap mode drawing
* REVIEWED: GuiScrollBar(), minor tweaks
+* REVIEWED: GuiProgressBar(), improved borders computing
+* REVIEWED: GuiTextBox(), multiple improvements: autocursor and more
* REVIEWED: Functions descriptions, removed wrong return value reference
* REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion
*
@@ -259,16 +266,16 @@
* 1.4 (15-Jun-2017) Rewritten all GUI functions (removed useless ones)
* 1.3 (12-Jun-2017) Complete redesign of style system
* 1.1 (01-Jun-2017) Complete review of the library
-* 1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria.
-* 0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria.
-* 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria.
+* 1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria
+* 0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria
+* 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria
*
* DEPENDENCIES:
-* raylib 5.0 - Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing
+* raylib 5.6-dev - Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing
*
* STANDALONE MODE:
* By default raygui depends on raylib mostly for the inputs and the drawing functionality but that dependency can be disabled
-* with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs.
+* with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs
*
* The following functions should be redefined for a custom backend:
*
@@ -309,7 +316,7 @@
*
* LICENSE: zlib/libpng
*
-* Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+* 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.
@@ -334,7 +341,7 @@
#define RAYGUI_VERSION_MAJOR 4
#define RAYGUI_VERSION_MINOR 5
#define RAYGUI_VERSION_PATCH 0
-#define RAYGUI_VERSION "4.5-dev"
+#define RAYGUI_VERSION "5.0-dev"
#if !defined(RAYGUI_STANDALONE)
#include "raylib.h"
@@ -358,17 +365,6 @@
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
-// Allow custom memory allocators
-#ifndef RAYGUI_MALLOC
- #define RAYGUI_MALLOC(sz) malloc(sz)
-#endif
-#ifndef RAYGUI_CALLOC
- #define RAYGUI_CALLOC(n,sz) calloc(n,sz)
-#endif
-#ifndef RAYGUI_FREE
- #define RAYGUI_FREE(p) free(p)
-#endif
-
// Simple log system to avoid printf() calls if required
// NOTE: Avoiding those calls, also avoids const strings memory usage
#define RAYGUI_SUPPORT_LOG_INFO
@@ -421,13 +417,16 @@
// TODO: Texture2D type is very coupled to raylib, required by Font type
// It should be redesigned to be provided by user
- typedef struct Texture2D {
+ typedef struct Texture {
unsigned int id; // OpenGL texture id
int width; // Texture base width
int height; // Texture base height
int mipmaps; // Mipmap levels, 1 by default
int format; // Data format (PixelFormat type)
- } Texture2D;
+ } Texture;
+
+ // Texture2D, same as Texture
+ typedef Texture Texture2D;
// Image, pixel data stored in CPU memory (RAM)
typedef struct Image {
@@ -527,7 +526,7 @@ typedef enum {
DROPDOWNBOX,
TEXTBOX, // Used also for: TEXTBOXMULTI
VALUEBOX,
- SPINNER, // Uses: BUTTON, VALUEBOX
+ CONTROL11,
LISTVIEW,
COLORPICKER,
SCROLLBAR,
@@ -549,12 +548,12 @@ typedef enum {
BORDER_COLOR_DISABLED, // Control border color in STATE_DISABLED
BASE_COLOR_DISABLED, // Control base color in STATE_DISABLED
TEXT_COLOR_DISABLED, // Control text color in STATE_DISABLED
- BORDER_WIDTH, // Control border size, 0 for no border
+ BORDER_WIDTH = 12, // Control border size, 0 for no border
//TEXT_SIZE, // Control text size (glyphs max height) -> GLOBAL for all controls
//TEXT_SPACING, // Control text spacing between glyphs -> GLOBAL for all controls
- //TEXT_LINE_SPACING // Control text spacing between lines -> GLOBAL for all controls
- TEXT_PADDING, // Control text padding, not considering border
- TEXT_ALIGNMENT, // Control text horizontal alignment inside control text bound (after border and padding)
+ //TEXT_LINE_SPACING, // Control text spacing between lines -> GLOBAL for all controls
+ TEXT_PADDING = 13, // Control text padding, not considering border
+ TEXT_ALIGNMENT = 14, // Control text horizontal alignment inside control text bound (after border and padding)
//TEXT_WRAP_MODE // Control text wrap-mode inside text bounds -> GLOBAL for all controls
} GuiControlProperty;
@@ -641,11 +640,14 @@ typedef enum {
TEXT_READONLY = 16, // TextBox in read-only mode: 0-text editable, 1-text no-editable
} GuiTextBoxProperty;
-// Spinner
+// ValueBox/Spinner
typedef enum {
- SPIN_BUTTON_WIDTH = 16, // Spinner left/right buttons width
- SPIN_BUTTON_SPACING, // Spinner buttons separation
-} GuiSpinnerProperty;
+ SPINNER_BUTTON_WIDTH = 16, // Spinner left/right buttons width
+ SPINNER_BUTTON_SPACING, // Spinner buttons separation
+} GuiValueBoxProperty;
+
+// Control11
+//typedef enum { } GuiControl11Property;
// ListView
typedef enum {
@@ -653,6 +655,7 @@ typedef enum {
LIST_ITEMS_SPACING, // ListView items separation
SCROLLBAR_WIDTH, // ListView scrollbar size (usually width)
SCROLLBAR_SIDE, // ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE)
+ LIST_ITEMS_BORDER_NORMAL, // ListView items border enabled in normal state
LIST_ITEMS_BORDER_WIDTH // ListView items border width
} GuiListViewProperty;
@@ -717,6 +720,9 @@ RAYGUIAPI char **GuiLoadIcons(const char *fileName, bool loadIconsName); // Load
RAYGUIAPI void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color); // Draw icon using pixel size at specified position
#endif
+// Utility functions
+RAYGUIAPI int GuiGetTextWidth(const char *text); // Get text width considering gui style and icon size (if required)
+
// Controls
//----------------------------------------------------------------------------------------------------------
// Container/separator controls, useful for controls organization
@@ -999,33 +1005,33 @@ typedef enum {
ICON_MLAYERS = 226,
ICON_MAPS = 227,
ICON_HOT = 228,
- ICON_229 = 229,
- ICON_230 = 230,
- ICON_231 = 231,
- ICON_232 = 232,
- ICON_233 = 233,
- ICON_234 = 234,
- ICON_235 = 235,
- ICON_236 = 236,
- ICON_237 = 237,
- ICON_238 = 238,
- ICON_239 = 239,
- ICON_240 = 240,
- ICON_241 = 241,
- ICON_242 = 242,
- ICON_243 = 243,
- ICON_244 = 244,
- ICON_245 = 245,
- ICON_246 = 246,
- ICON_247 = 247,
- ICON_248 = 248,
- ICON_249 = 249,
+ ICON_LABEL = 229,
+ ICON_NAME_ID = 230,
+ ICON_SLICING = 231,
+ ICON_MANUAL_CONTROL = 232,
+ ICON_COLLISION = 233,
+ ICON_CIRCLE_ADD = 234,
+ ICON_CIRCLE_ADD_FILL = 235,
+ ICON_CIRCLE_WARNING = 236,
+ ICON_CIRCLE_WARNING_FILL = 237,
+ ICON_BOX_MORE = 238,
+ ICON_BOX_MORE_FILL = 239,
+ ICON_BOX_MINUS = 240,
+ ICON_BOX_MINUS_FILL = 241,
+ ICON_UNION = 242,
+ ICON_INTERSECTION = 243,
+ ICON_DIFFERENCE = 244,
+ ICON_SPHERE = 245,
+ ICON_CYLINDER = 246,
+ ICON_CONE = 247,
+ ICON_ELLIPSOID = 248,
+ ICON_CAPSULE = 249,
ICON_250 = 250,
ICON_251 = 251,
ICON_252 = 252,
ICON_253 = 253,
ICON_254 = 254,
- ICON_255 = 255,
+ ICON_255 = 255
} GuiIconName;
#endif
@@ -1046,12 +1052,24 @@ typedef enum {
#if defined(RAYGUI_IMPLEMENTATION)
#include // required for: isspace() [GuiTextBox()]
-#include // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()]
-#include // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]
+#include // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), snprintf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()]
#include // Required for: strlen() [GuiTextBox(), GuiValueBox()], memset(), memcpy()
#include // Required for: va_list, va_start(), vfprintf(), va_end() [TextFormat()]
#include // Required for: roundf() [GuiColorPicker()]
+// Allow custom memory allocators
+#if defined(RAYGUI_MALLOC) || defined(RAYGUI_CALLOC) || defined(RAYGUI_FREE)
+ #if !defined(RAYGUI_MALLOC) || !defined(RAYGUI_CALLOC) || !defined(RAYGUI_FREE)
+ #error "RAYGUI: if RAYGUI_MALLOC, RAYGUI_CALLOC, or RAYGUI_FREE is customized, all three must be customized"
+ #endif
+#else
+ #include // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]
+
+ #define RAYGUI_MALLOC(sz) malloc(sz)
+ #define RAYGUI_CALLOC(n,sz) calloc(n,sz)
+ #define RAYGUI_FREE(p) free(p)
+#endif
+
#ifdef __cplusplus
#define RAYGUI_CLITERAL(name) name
#else
@@ -1060,7 +1078,7 @@ typedef enum {
// Check if two rectangles are equal, used to validate a slider bounds as an id
#ifndef CHECK_BOUNDS_ID
- #define CHECK_BOUNDS_ID(src, dst) ((src.x == dst.x) && (src.y == dst.y) && (src.width == dst.width) && (src.height == dst.height))
+ #define CHECK_BOUNDS_ID(src, dst) (((int)src.x == (int)dst.x) && ((int)src.y == (int)dst.y) && ((int)src.width == (int)dst.width) && ((int)src.height == (int)dst.height))
#endif
#if !defined(RAYGUI_NO_ICONS) && !defined(RAYGUI_CUSTOM_ICONS)
@@ -1318,27 +1336,27 @@ static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] =
0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x422a422a, 0x422e422a, 0x40384e28, 0x00007fe0, // ICON_MLAYERS
0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x5b2a512a, 0x512e552a, 0x40385128, 0x00007fe0, // ICON_MAPS
0x04200000, 0x1cf00c60, 0x11f019f0, 0x0f3807b8, 0x1e3c0f3c, 0x1c1c1e1c, 0x1e3c1c1c, 0x00000f70, // ICON_HOT
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_229
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_230
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_231
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_232
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_233
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_234
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_235
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_236
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_237
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_238
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_239
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_240
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_241
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_242
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_243
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_244
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_245
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_246
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_247
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_248
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_249
+ 0x00000000, 0x20803f00, 0x2a202e40, 0x20082e10, 0x08021004, 0x02040402, 0x00900108, 0x00000060, // ICON_LABEL
+ 0x00000000, 0x042007e0, 0x47e27c3e, 0x4ffa4002, 0x47fa4002, 0x4ffa4002, 0x7ffe4002, 0x00000000, // ICON_NAME_ID
+ 0x7fe00000, 0x402e4020, 0x43ce5e0a, 0x40504078, 0x438e4078, 0x402e5e0a, 0x7fe04020, 0x00000000, // ICON_SLICING
+ 0x00000000, 0x40027ffe, 0x47c24002, 0x55425d42, 0x55725542, 0x50125552, 0x10105016, 0x00001ff0, // ICON_MANUAL_CONTROL
+ 0x7ffe0000, 0x43c24002, 0x48124422, 0x500a500a, 0x500a500a, 0x44224812, 0x400243c2, 0x00007ffe, // ICON_COLLISION
+ 0x03c00000, 0x10080c30, 0x21842184, 0x4ff24182, 0x41824ff2, 0x21842184, 0x0c301008, 0x000003c0, // ICON_CIRCLE_ADD
+ 0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x700e7e7e, 0x7e7e700e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0, // ICON_CIRCLE_ADD_FILL
+ 0x03c00000, 0x10080c30, 0x21842184, 0x41824182, 0x40024182, 0x21842184, 0x0c301008, 0x000003c0, // ICON_CIRCLE_WARNING
+ 0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x7e7e7e7e, 0x7ffe7e7e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0, // ICON_CIRCLE_WARNING_FILL
+ 0x00000000, 0x10041ffc, 0x10841004, 0x13e41084, 0x10841084, 0x10041004, 0x00001ffc, 0x00000000, // ICON_BOX_MORE
+ 0x00000000, 0x1ffc1ffc, 0x1f7c1ffc, 0x1c1c1f7c, 0x1f7c1f7c, 0x1ffc1ffc, 0x00001ffc, 0x00000000, // ICON_BOX_MORE_FILL
+ 0x00000000, 0x1ffc1ffc, 0x1ffc1ffc, 0x1c1c1ffc, 0x1ffc1ffc, 0x1ffc1ffc, 0x00001ffc, 0x00000000, // ICON_BOX_MINUS
+ 0x00000000, 0x10041ffc, 0x10041004, 0x13e41004, 0x10041004, 0x10041004, 0x00001ffc, 0x00000000, // ICON_BOX_MINUS_FILL
+ 0x07fe0000, 0x055606aa, 0x7ff606aa, 0x55766eba, 0x55766eaa, 0x55606ffe, 0x55606aa0, 0x00007fe0, // ICON_UNION
+ 0x07fe0000, 0x04020402, 0x7fe20402, 0x456246a2, 0x456246a2, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_INTERSECTION
+ 0x07fe0000, 0x055606aa, 0x7ff606aa, 0x4436442a, 0x4436442a, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_DIFFERENCE
+ 0x03c00000, 0x10080c30, 0x20042004, 0x60064002, 0x47e2581a, 0x20042004, 0x0c301008, 0x000003c0, // ICON_SPHERE
+ 0x03e00000, 0x08080410, 0x0c180808, 0x08080be8, 0x08080808, 0x08080808, 0x04100808, 0x000003e0, // ICON_CYLINDER
+ 0x00800000, 0x01400140, 0x02200220, 0x04100410, 0x08080808, 0x1c1c13e4, 0x08081004, 0x000007f0, // ICON_CONE
+ 0x00000000, 0x07e00000, 0x20841918, 0x40824082, 0x40824082, 0x19182084, 0x000007e0, 0x00000000, // ICON_ELLIPSOID
+ 0x00000000, 0x00000000, 0x20041ff8, 0x40024002, 0x40024002, 0x1ff82004, 0x00000000, 0x00000000, // ICON_CAPSULE
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_250
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_251
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_252
@@ -1363,7 +1381,7 @@ static unsigned int *guiIconsPtr = guiIcons;
#define RAYGUI_MAX_PROPS_EXTENDED 8 // Maximum number of extended properties
//----------------------------------------------------------------------------------
-// Types and Structures Definition
+// Module Types and Structures Definition
//----------------------------------------------------------------------------------
// Gui control property style color element
typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement;
@@ -1387,8 +1405,7 @@ static Rectangle guiControlExclusiveRec = { 0 }; // Gui control exclusive bounds
static int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*()
//static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking
-static int autoCursorCooldownCounter = 0; // Cooldown frame counter for automatic cursor movement on key-down
-static int autoCursorDelayCounter = 0; // Delay frame counter for automatic cursor movement
+static int autoCursorCounter = 0; // Frame counter for automatic repeated cursor movement on key-down (cooldown and delay)
//----------------------------------------------------------------------------------
// Style data array for all gui style properties (allocated on data segment by default)
@@ -1484,7 +1501,6 @@ static void DrawRectangleGradientV(int posX, int posY, int width, int height, Co
//----------------------------------------------------------------------------------
static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize); // Load style from memory (binary only)
-static int GetTextWidth(const char *text); // Gui get text width using gui font and style
static Rectangle GetTextBounds(int control, Rectangle bounds); // Get text bounds considering control bounds
static const char *GetTextIcon(const char *text, int *iconId); // Get text icon if provided and move text cursor
@@ -1589,6 +1605,10 @@ int GuiWindowBox(Rectangle bounds, const char *title)
#define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT 24
#endif
+ #if !defined(RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT)
+ #define RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT 18
+ #endif
+
int result = 0;
//GuiState state = guiState;
@@ -1597,9 +1617,10 @@ int GuiWindowBox(Rectangle bounds, const char *title)
Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)statusBarHeight };
if (bounds.height < statusBarHeight*2.0f) bounds.height = statusBarHeight*2.0f;
+ const float vPadding = statusBarHeight/2.0f - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT/2.0f;
Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - 1, bounds.width, bounds.height - (float)statusBarHeight + 1 };
- Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - 20,
- statusBar.y + statusBarHeight/2.0f - 18.0f/2.0f, 18, 18 };
+ Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding,
+ statusBar.y + vPadding, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT };
// Update control
//--------------------------------------------------------------------
@@ -1653,7 +1674,7 @@ int GuiGroupBox(Rectangle bounds, const char *text)
// Line control
int GuiLine(Rectangle bounds, const char *text)
{
- #if !defined(RAYGUI_LINE_ORIGIN_SIZE)
+ #if !defined(RAYGUI_LINE_MARGIN_TEXT)
#define RAYGUI_LINE_MARGIN_TEXT 12
#endif
#if !defined(RAYGUI_LINE_TEXT_PADDING)
@@ -1671,7 +1692,7 @@ int GuiLine(Rectangle bounds, const char *text)
else
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(text) + 2;
+ textBounds.width = (float)GuiGetTextWidth(text) + 2;
textBounds.height = bounds.height;
textBounds.x = bounds.x + RAYGUI_LINE_MARGIN_TEXT;
textBounds.y = bounds.y;
@@ -1711,8 +1732,8 @@ int GuiPanel(Rectangle bounds, const char *text)
//--------------------------------------------------------------------
if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar
- GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED: (int)LINE_COLOR)),
- GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR)));
+ GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)),
+ GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BASE_COLOR_DISABLED : (int)BACKGROUND_COLOR)));
//--------------------------------------------------------------------
return result;
@@ -1722,7 +1743,7 @@ int GuiPanel(Rectangle bounds, const char *text)
// NOTE: Using GuiToggle() for the TABS
int GuiTabBar(Rectangle bounds, const char **text, int count, int *active)
{
- #define RAYGUI_TABBAR_ITEM_WIDTH 160
+ #define RAYGUI_TABBAR_ITEM_WIDTH 148
int result = -1;
//GuiState state = guiState;
@@ -1755,12 +1776,12 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active)
if (i == (*active))
{
toggle = true;
- GuiToggle(tabBounds, GuiIconText(12, text[i]), &toggle);
+ GuiToggle(tabBounds, text[i], &toggle);
}
else
{
toggle = false;
- GuiToggle(tabBounds, GuiIconText(12, text[i]), &toggle);
+ GuiToggle(tabBounds, text[i], &toggle);
if (toggle) *active = i;
}
@@ -2011,7 +2032,7 @@ int GuiLabelButton(Rectangle bounds, const char *text)
bool pressed = false;
// NOTE: We force bounds.width to be all text
- float textWidth = (float)GetTextWidth(text);
+ float textWidth = (float)GuiGetTextWidth(text);
if ((bounds.width - 2*GuiGetStyle(LABEL, BORDER_WIDTH) - 2*GuiGetStyle(LABEL, TEXT_PADDING)) < textWidth) bounds.width = textWidth + 2*GuiGetStyle(LABEL, BORDER_WIDTH) + 2*GuiGetStyle(LABEL, TEXT_PADDING) + 2;
// Update control
@@ -2149,7 +2170,9 @@ int GuiToggleSlider(Rectangle bounds, const char *text, int *active)
// Get substrings items from text (items pointers)
int itemCount = 0;
- const char **items = GuiTextSplit(text, ';', &itemCount, NULL);
+ const char **items = NULL;
+
+ if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL);
Rectangle slider = {
0, // Calculated later depending on the active toggle
@@ -2196,7 +2219,7 @@ int GuiToggleSlider(Rectangle bounds, const char *text, int *active)
if (text != NULL)
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(text);
+ textBounds.width = (float)GuiGetTextWidth(text);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = slider.x + slider.width/2 - textBounds.width/2;
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -2221,7 +2244,7 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked)
if (text != NULL)
{
- textBounds.width = (float)GetTextWidth(text) + 2;
+ textBounds.width = (float)GuiGetTextWidth(text) + 2;
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -2474,7 +2497,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod
int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
{
#if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)
- #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 40 // Frames to wait for autocursor movement
+ #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 20 // Frames to wait for autocursor movement
#endif
#if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY)
#define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY 1 // Frames delay for autocursor movement
@@ -2487,10 +2510,10 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE);
Rectangle textBounds = GetTextBounds(TEXTBOX, bounds);
- int textLength = (int)strlen(text); // Get current text length
+ int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length
int thisCursorIndex = textBoxCursorIndex;
if (thisCursorIndex > textLength) thisCursorIndex = textLength;
- int textWidth = GetTextWidth(text) - GetTextWidth(text + thisCursorIndex);
+ int textWidth = GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex);
int textIndexOffset = 0; // Text index offset to start drawing in the box
// Cursor rectangle
@@ -2511,15 +2534,6 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
mouseCursor.x = -1;
mouseCursor.width = 1;
- // Auto-cursor movement logic
- // NOTE: Cursor moves automatically when key down after some time
- if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCooldownCounter++;
- else
- {
- autoCursorCooldownCounter = 0; // GLOBAL: Cursor cooldown counter
- autoCursorDelayCounter = 0; // GLOBAL: Cursor delay counter
- }
-
// Blink-cursor frame counter
//if (!autoCursorMode) blinkCursorFrameCounter++;
//else blinkCursorFrameCounter = 0;
@@ -2537,6 +2551,13 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
if (editMode)
{
+ // GLOBAL: Auto-cursor movement logic
+ // NOTE: Keystrokes are handled repeatedly when button is held down for some time
+ if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCounter++;
+ else autoCursorCounter = 0;
+
+ bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0);
+
state = STATE_PRESSED;
if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
@@ -2550,7 +2571,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
textIndexOffset += nextCodepointSize;
- textWidth = GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex);
+ textWidth = GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex);
}
int codepoint = GetCharPressed(); // Get Unicode codepoint
@@ -2560,10 +2581,43 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
int codepointSize = 0;
const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize);
- // Add codepoint to text, at current cursor position
- // NOTE: Make sure we do not overflow buffer size
- if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize))
+ // Handle text paste action
+ if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
{
+ const char *pasteText = GetClipboardText();
+ if (pasteText != NULL)
+ {
+ int pasteLength = 0;
+ int pasteCodepoint;
+ int pasteCodepointSize;
+
+ // Count how many codepoints to copy, stopping at the first unwanted control character
+ while (true)
+ {
+ pasteCodepoint = GetCodepointNext(pasteText + pasteLength, &pasteCodepointSize);
+ if (textLength + pasteLength + pasteCodepointSize >= textSize) break;
+ if (!(multiline && (pasteCodepoint == (int)'\n')) && !(pasteCodepoint >= 32)) break;
+ pasteLength += pasteCodepointSize;
+ }
+
+ if (pasteLength > 0)
+ {
+ // Move forward data from cursor position
+ for (int i = textLength + pasteLength; i > textBoxCursorIndex; i--) text[i] = text[i - pasteLength];
+
+ // Paste data in at cursor
+ for (int i = 0; i < pasteLength; i++) text[textBoxCursorIndex + i] = pasteText[i];
+
+ textBoxCursorIndex += pasteLength;
+ textLength += pasteLength;
+ text[textLength] = '\0';
+ }
+ }
+ }
+ else if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize))
+ {
+ // Adding codepoint to text, at current cursor position
+
// Move forward data from cursor position
for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize];
@@ -2583,113 +2637,185 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
// Move cursor to end
if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) textBoxCursorIndex = textLength;
- // Delete codepoint from text, after current cursor position
- if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))))
+ // Delete related codepoints from text, after current cursor position
+ if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
{
- autoCursorDelayCounter++;
+ int offset = textBoxCursorIndex;
+ int accCodepointSize = 0;
+ int nextCodepointSize;
+ int nextCodepoint;
- if (IsKeyPressed(KEY_DELETE) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames
+ // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
+ // Not using isalnum() since it only works on ASCII characters
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+ bool puctuation = ispunct(nextCodepoint & 0xff);
+ while (offset < textLength)
{
- int nextCodepointSize = 0;
- GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
-
- // Move backward text from cursor position
- for (int i = textBoxCursorIndex; i < textLength; i++) text[i] = text[i + nextCodepointSize];
-
- textLength -= codepointSize;
- if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
-
- // Make sure text last character is EOL
- text[textLength] = '\0';
+ if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff))))
+ break;
+ offset += nextCodepointSize;
+ accCodepointSize += nextCodepointSize;
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
}
+
+ // Check whitespace to delete (ASCII only)
+ while (offset < textLength)
+ {
+ if (!isspace(nextCodepoint & 0xff)) break;
+
+ offset += nextCodepointSize;
+ accCodepointSize += nextCodepointSize;
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+ }
+
+ // Move text after cursor forward (including final null terminator)
+ for (int i = offset; i <= textLength; i++) text[i - accCodepointSize] = text[i];
+
+ textLength -= accCodepointSize;
+ }
+
+ else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger)))
+ {
+ // Delete single codepoint from text, after current cursor position
+
+ int nextCodepointSize = 0;
+ GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+
+ // Move text after cursor forward (including final null terminator)
+ for (int i = textBoxCursorIndex + nextCodepointSize; i <= textLength; i++) text[i - nextCodepointSize] = text[i];
+
+ textLength -= nextCodepointSize;
}
// Delete related codepoints from text, before current cursor position
- if ((textLength > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+ if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
{
- int i = textBoxCursorIndex - 1;
+ int offset = textBoxCursorIndex;
int accCodepointSize = 0;
+ int prevCodepointSize;
+ int prevCodepoint;
- // Move cursor to the end of word if on space already
- while ((i > 0) && isspace(text[i]))
+ // Check whitespace to delete (ASCII only)
+ while (offset > 0)
{
- int prevCodepointSize = 0;
- GetCodepointPrevious(text + i, &prevCodepointSize);
- i -= prevCodepointSize;
+ prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+ if (!isspace(prevCodepoint & 0xff)) break;
+
+ offset -= prevCodepointSize;
accCodepointSize += prevCodepointSize;
}
- // Move cursor to the start of the word
- while ((i > 0) && !isspace(text[i]))
+ // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
+ // Not using isalnum() since it only works on ASCII characters
+ bool puctuation = ispunct(prevCodepoint & 0xff);
+ while (offset > 0)
{
- int prevCodepointSize = 0;
- GetCodepointPrevious(text + i, &prevCodepointSize);
- i -= prevCodepointSize;
+ prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+ if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
+
+ offset -= prevCodepointSize;
accCodepointSize += prevCodepointSize;
}
- // Move forward text from cursor position
- for (int j = (textBoxCursorIndex - accCodepointSize); j < textLength; j++) text[j] = text[j + accCodepointSize];
+ // Move text after cursor forward (including final null terminator)
+ for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - accCodepointSize] = text[i];
- // Prevent cursor index from decrementing past 0
- if (textBoxCursorIndex > 0)
- {
- textBoxCursorIndex -= accCodepointSize;
- textLength -= accCodepointSize;
- }
+ textLength -= accCodepointSize;
+ textBoxCursorIndex -= accCodepointSize;
+ }
- // Make sure text last character is EOL
- text[textLength] = '\0';
- }
- else if ((textLength > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))))
+ else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger)))
{
- autoCursorDelayCounter++;
+ // Delete single codepoint from text, before current cursor position
- if (IsKeyPressed(KEY_BACKSPACE) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames
- {
- int prevCodepointSize = 0;
+ int prevCodepointSize = 0;
- // Prevent cursor index from decrementing past 0
- if (textBoxCursorIndex > 0)
- {
- GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+ GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
- // Move backward text from cursor position
- for (int i = (textBoxCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize];
+ // Move text after cursor forward (including final null terminator)
+ for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - prevCodepointSize] = text[i];
- textBoxCursorIndex -= codepointSize;
- textLength -= codepointSize;
- }
-
- // Make sure text last character is EOL
- text[textLength] = '\0';
- }
+ textLength -= prevCodepointSize;
+ textBoxCursorIndex -= prevCodepointSize;
}
// Move cursor position with keys
- if (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))
+ if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
{
- autoCursorDelayCounter++;
+ int offset = textBoxCursorIndex;
+ //int accCodepointSize = 0;
+ int prevCodepointSize;
+ int prevCodepoint;
- if (IsKeyPressed(KEY_LEFT) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames
+ // Check whitespace to skip (ASCII only)
+ while (offset > 0)
{
- int prevCodepointSize = 0;
- if (textBoxCursorIndex > 0) GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+ prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+ if (!isspace(prevCodepoint & 0xff)) break;
- if (textBoxCursorIndex >= prevCodepointSize) textBoxCursorIndex -= prevCodepointSize;
+ offset -= prevCodepointSize;
+ //accCodepointSize += prevCodepointSize;
}
+
+ // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
+ // Not using isalnum() since it only works on ASCII characters
+ bool puctuation = ispunct(prevCodepoint & 0xff);
+ while (offset > 0)
+ {
+ prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+ if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
+
+ offset -= prevCodepointSize;
+ //accCodepointSize += prevCodepointSize;
+ }
+
+ textBoxCursorIndex = offset;
}
- else if (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))
+ else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger)))
{
- autoCursorDelayCounter++;
+ int prevCodepointSize = 0;
+ GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
- if (IsKeyPressed(KEY_RIGHT) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames
+ textBoxCursorIndex -= prevCodepointSize;
+ }
+ else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+ {
+ int offset = textBoxCursorIndex;
+ //int accCodepointSize = 0;
+ int nextCodepointSize;
+ int nextCodepoint;
+
+ // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
+ // Not using isalnum() since it only works on ASCII characters
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+ bool puctuation = ispunct(nextCodepoint & 0xff);
+ while (offset < textLength)
{
- int nextCodepointSize = 0;
- GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+ if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) break;
- if ((textBoxCursorIndex + nextCodepointSize) <= textLength) textBoxCursorIndex += nextCodepointSize;
+ offset += nextCodepointSize;
+ //accCodepointSize += nextCodepointSize;
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
}
+
+ // Check whitespace to skip (ASCII only)
+ while (offset < textLength)
+ {
+ if (!isspace(nextCodepoint & 0xff)) break;
+
+ offset += nextCodepointSize;
+ //accCodepointSize += nextCodepointSize;
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+ }
+
+ textBoxCursorIndex = offset;
+ }
+ else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger)))
+ {
+ int nextCodepointSize = 0;
+ GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+
+ textBoxCursorIndex += nextCodepointSize;
}
// Move cursor position with mouse
@@ -2701,7 +2827,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
float widthToMouseX = 0;
int mouseCursorIndex = 0;
- for (int i = textIndexOffset; i < textLength; i++)
+ for (int i = textIndexOffset; i < textLength; i += codepointSize)
{
codepoint = GetCodepointNext(&text[i], &codepointSize);
codepointIndex = GetGlyphIndex(guiFont, codepoint);
@@ -2720,7 +2846,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
}
// Check if mouse cursor is at the last position
- int textEndWidth = GetTextWidth(text + textIndexOffset);
+ int textEndWidth = GuiGetTextWidth(text + textIndexOffset);
if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2))
{
mouseCursor.x = textBounds.x + textEndWidth;
@@ -2737,7 +2863,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
else mouseCursor.x = -1;
// Recalculate cursor position.y depending on textBoxCursorIndex
- cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
+ cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
//if (multiline) cursor.y = GetTextLines()
// Finish text editing on ENTER or mouse click outside bounds
@@ -2745,6 +2871,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
(!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
{
textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index
+ autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes
result = 1;
}
}
@@ -2757,6 +2884,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
{
textBoxCursorIndex = textLength; // GLOBAL: Place cursor index to the end of current text
+ autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes
result = 1;
}
}
@@ -2825,19 +2953,22 @@ int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int
int tempValue = *value;
- Rectangle spinner = { bounds.x + GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SPIN_BUTTON_SPACING), bounds.y,
- bounds.width - 2*(GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SPIN_BUTTON_SPACING)), bounds.height };
- Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.height };
- Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.y, (float)GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.height };
+ Rectangle valueBoxBounds = {
+ bounds.x + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING),
+ bounds.y,
+ bounds.width - 2*(GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING)), bounds.height };
+ Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height };
+ Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.y,
+ (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height };
Rectangle textBounds = { 0 };
if (text != NULL)
{
- textBounds.width = (float)GetTextWidth(text) + 2;
+ textBounds.width = (float)GuiGetTextWidth(text) + 2;
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
- textBounds.x = bounds.x + bounds.width + GuiGetStyle(SPINNER, TEXT_PADDING);
+ textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
- if (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SPINNER, TEXT_PADDING);
+ if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
}
// Update control
@@ -2871,20 +3002,20 @@ int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int
// Draw control
//--------------------------------------------------------------------
- result = GuiValueBox(spinner, NULL, &tempValue, minValue, maxValue, editMode);
+ result = GuiValueBox(valueBoxBounds, NULL, &tempValue, minValue, maxValue, editMode);
// Draw value selector custom buttons
// NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
- GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(SPINNER, BORDER_WIDTH));
+ GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(VALUEBOX, BORDER_WIDTH));
GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
// Draw text label if provided
- GuiDrawText(text, textBounds, (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+ GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
//--------------------------------------------------------------------
*value = tempValue;
@@ -2902,13 +3033,13 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in
int result = 0;
GuiState state = guiState;
- char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0";
- sprintf(textValue, "%i", *value);
+ char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = { 0 };
+ snprintf(textValue, RAYGUI_VALUEBOX_MAX_CHARS + 1, "%i", *value);
Rectangle textBounds = { 0 };
if (text != NULL)
{
- textBounds.width = (float)GetTextWidth(text) + 2;
+ textBounds.width = (float)GuiGetTextWidth(text) + 2;
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -2920,7 +3051,6 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in
if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
{
Vector2 mousePoint = GetMousePosition();
-
bool valueHasChanged = false;
if (editMode)
@@ -2929,30 +3059,53 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in
int keyCount = (int)strlen(textValue);
- // Only allow keys in range [48..57]
- if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+ // Add or remove minus symbol
+ if (IsKeyPressed(KEY_MINUS))
{
- if (GetTextWidth(textValue) < bounds.width)
+ if (textValue[0] == '-')
{
- int key = GetCharPressed();
- if ((key >= 48) && (key <= 57))
+ for (int i = 0 ; i < keyCount; i++) textValue[i] = textValue[i + 1];
+
+ keyCount--;
+ valueHasChanged = true;
+ }
+ else if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+ {
+ if (keyCount == 0)
{
- textValue[keyCount] = (char)key;
+ textValue[0] = '0';
+ textValue[1] = '\0';
keyCount++;
- valueHasChanged = true;
}
+
+ for (int i = keyCount ; i > -1; i--) textValue[i + 1] = textValue[i];
+
+ textValue[0] = '-';
+ keyCount++;
+ valueHasChanged = true;
+ }
+ }
+
+ // Add new digit to text value
+ if ((keyCount >= 0) && (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) && (GuiGetTextWidth(textValue) < bounds.width))
+ {
+ int key = GetCharPressed();
+
+ // Only allow keys in range [48..57]
+ if ((key >= 48) && (key <= 57))
+ {
+ textValue[keyCount] = (char)key;
+ keyCount++;
+ valueHasChanged = true;
}
}
// Delete text
- if (keyCount > 0)
+ if ((keyCount > 0) && IsKeyPressed(KEY_BACKSPACE))
{
- if (IsKeyPressed(KEY_BACKSPACE))
- {
- keyCount--;
- textValue[keyCount] = '\0';
- valueHasChanged = true;
- }
+ keyCount--;
+ textValue[keyCount] = '\0';
+ valueHasChanged = true;
}
if (valueHasChanged) *value = TextToInteger(textValue);
@@ -2992,11 +3145,14 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in
GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor);
GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))));
- // Draw cursor
+ // Draw cursor rectangle
if (editMode)
{
// NOTE: ValueBox internal text is always centered
- Rectangle cursor = { bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 1, bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4, bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH) };
+ Rectangle cursor = { bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1,
+ bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + 2,
+ 2, bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2 - 4 };
+ if (cursor.height > bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2;
GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
}
@@ -3019,12 +3175,12 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float
GuiState state = guiState;
//char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0";
- //sprintf(textValue, "%2.2f", *value);
+ //snprintf(textValue, sizeof(textValue), "%2.2f", *value);
- Rectangle textBounds = {0};
+ Rectangle textBounds = { 0 };
if (text != NULL)
{
- textBounds.width = (float)GetTextWidth(text) + 2;
+ textBounds.width = (float)GuiGetTextWidth(text) + 2;
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -3045,10 +3201,37 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float
int keyCount = (int)strlen(textValue);
+ // Add or remove minus symbol
+ if (IsKeyPressed(KEY_MINUS))
+ {
+ if (textValue[0] == '-')
+ {
+ for (int i = 0; i < keyCount; i++) textValue[i] = textValue[i + 1];
+
+ keyCount--;
+ valueHasChanged = true;
+ }
+ else if (keyCount < (RAYGUI_VALUEBOX_MAX_CHARS - 1))
+ {
+ if (keyCount == 0)
+ {
+ textValue[0] = '0';
+ textValue[1] = '\0';
+ keyCount++;
+ }
+
+ for (int i = keyCount; i > -1; i--) textValue[i + 1] = textValue[i];
+
+ textValue[0] = '-';
+ keyCount++;
+ valueHasChanged = true;
+ }
+ }
+
// Only allow keys in range [48..57]
if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
{
- if (GetTextWidth(textValue) < bounds.width)
+ if (GuiGetTextWidth(textValue) < bounds.width)
{
int key = GetCharPressed();
if (((key >= 48) && (key <= 57)) ||
@@ -3103,7 +3286,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float
if (editMode)
{
// NOTE: ValueBox internal text is always centered
- Rectangle cursor = {bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 1,
+ Rectangle cursor = {bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1,
bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4,
bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH)};
GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
@@ -3120,7 +3303,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float
// Slider control with pro parameters
// NOTE: Other GuiSlider*() controls use this one
-int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth)
+int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
{
int result = 0;
GuiState state = guiState;
@@ -3129,6 +3312,8 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight,
if (value == NULL) value = &temp;
float oldValue = *value;
+ int sliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH);
+
Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) };
@@ -3146,7 +3331,7 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight,
{
state = STATE_PRESSED;
// Get equivalent value and slider position from mousePosition.x
- *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width-sliderWidth)) + minValue;
+ *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue;
}
}
else
@@ -3166,7 +3351,7 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight,
if (!CheckCollisionPointRec(mousePoint, slider))
{
// Get equivalent value and slider position from mousePosition.x
- *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width-sliderWidth)) + minValue;
+ *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue;
}
}
else state = STATE_FOCUSED;
@@ -3205,44 +3390,45 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight,
if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)));
else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)));
else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_PRESSED)));
+ else if (state == STATE_DISABLED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_DISABLED)));
// Draw left/right text if provided
if (textLeft != NULL)
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(textLeft);
+ textBounds.width = (float)GuiGetTextWidth(textLeft);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
- GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))));
+ GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
}
if (textRight != NULL)
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(textRight);
+ textBounds.width = (float)GuiGetTextWidth(textRight);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
- GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))));
+ GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
}
//--------------------------------------------------------------------
return result;
}
-// Slider control extended, returns selected value and has text
-int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
-{
- return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH));
-}
-
// Slider Bar control extended, returns selected value
int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
{
- return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, 0);
+ int result = 0;
+ int preSliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH);
+ GuiSetStyle(SLIDER, SLIDER_WIDTH, 0);
+ result = GuiSlider(bounds, textLeft, textRight, value, minValue, maxValue);
+ GuiSetStyle(SLIDER, SLIDER_WIDTH, preSliderWidth);
+
+ return result;
}
// Progress Bar control extended, shows current progress value
@@ -3257,14 +3443,14 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight
// Progress bar
Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH),
bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), 0,
- bounds.height - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) };
+ bounds.height - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) -1 };
// Update control
//--------------------------------------------------------------------
if (*value > maxValue) *value = maxValue;
// WARNING: Working with floats could lead to rounding issues
- if ((state != STATE_DISABLED)) progress.width = (float)(*value/(maxValue - minValue))*bounds.width - ((*value >= maxValue)? (float)(2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)) : 0.0f);
+ if ((state != STATE_DISABLED)) progress.width = ((float)*value/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH));
//--------------------------------------------------------------------
// Draw control
@@ -3282,15 +3468,15 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight
GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
}
- else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+ else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
- if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + 1, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+ if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1}, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
else
{
// Draw borders not yet reached by value
- GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + 1, bounds.y, bounds.width - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
- GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + 1, bounds.y + bounds.height - 1, bounds.width - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
- GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+ GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+ GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y + bounds.height - 1, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+ GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
}
// Draw slider internal progress bar (depends on state)
@@ -3301,23 +3487,23 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight
if (textLeft != NULL)
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(textLeft);
+ textBounds.width = (float)GuiGetTextWidth(textLeft);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x - textBounds.width - GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
- GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))));
+ GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
}
if (textRight != NULL)
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(textRight);
+ textBounds.width = (float)GuiGetTextWidth(textRight);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
- GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))));
+ GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
}
//--------------------------------------------------------------------
@@ -3467,11 +3653,11 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd
// Draw visible items
for (int i = 0; ((i < visibleItems) && (text != NULL)); i++)
{
- GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK);
+ if (GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_NORMAL)) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK);
if (state == STATE_DISABLED)
{
- if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)));
+ if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)));
GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)));
}
@@ -3480,18 +3666,18 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd
if (((startIndex + i) == itemSelected) && (active != NULL))
{
// Draw item selected
- GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)));
+ GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)));
GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED)));
}
else if (((startIndex + i) == itemFocused)) // && (focus != NULL)) // NOTE: We want items focused, despite not returned!
{
// Draw item focused
- GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)));
+ GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)));
GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED)));
}
else
{
- // Draw item normal
+ // Draw item normal (no rectangle)
GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)));
}
}
@@ -3531,22 +3717,22 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd
return result;
}
-// Color Panel control - Color (RGBA) variant.
+// Color Panel control - Color (RGBA) variant
int GuiColorPanel(Rectangle bounds, const char *text, Color *color)
{
int result = 0;
Vector3 vcolor = { (float)color->r/255.0f, (float)color->g/255.0f, (float)color->b/255.0f };
Vector3 hsv = ConvertRGBtoHSV(vcolor);
- Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv.
+ Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv
GuiColorPanelHSV(bounds, text, &hsv);
- // Check if the hsv was changed, only then change the color.
- // This is required, because the Color->HSV->Color conversion has precision errors.
- // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value.
- // Otherwise GuiColorPanel would often modify it's color without user input.
- // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check.
+ // Check if the hsv was changed, only then change the color
+ // This is required, because the Color->HSV->Color conversion has precision errors
+ // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value
+ // Otherwise GuiColorPanel would often modify it's color without user input
+ // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check
if (hsv.x != prevHsv.x || hsv.y != prevHsv.y || hsv.z != prevHsv.z)
{
Vector3 rgb = ConvertHSVtoRGB(hsv);
@@ -3570,7 +3756,10 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
int result = 0;
GuiState state = guiState;
- Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT), (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 };
+ Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2,
+ (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW),
+ (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT),
+ (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 };
// Update control
//--------------------------------------------------------------------
@@ -3617,7 +3806,6 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
// Draw control
//--------------------------------------------------------------------
-
// Draw alpha bar: checked background
if (state != STATE_DISABLED)
{
@@ -3755,7 +3943,7 @@ int GuiColorPicker(Rectangle bounds, const char *text, Color *color)
Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height };
//Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) };
- // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used.
+ // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used
Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ (*color).r/255.0f, (*color).g/255.0f, (*color).b/255.0f });
GuiColorBarHue(boundsHue, NULL, &hsv.x);
@@ -3768,8 +3956,8 @@ int GuiColorPicker(Rectangle bounds, const char *text, Color *color)
return result;
}
-// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering.
-// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB.
+// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering
+// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB
// NOTE: It's divided in multiple controls:
// int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
// int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
@@ -3917,7 +4105,7 @@ int GuiMessageBox(Rectangle bounds, const char *title, const char *message, cons
buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount;
buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT;
- //int textWidth = GetTextWidth(message) + 2;
+ //int textWidth = GuiGetTextWidth(message) + 2;
Rectangle textBounds = { 0 };
textBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
@@ -3981,7 +4169,7 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co
Rectangle textBounds = { 0 };
if (message != NULL)
{
- int textSize = GetTextWidth(message) + 2;
+ int textSize = GuiGetTextWidth(message) + 2;
textBounds.x = bounds.x + bounds.width/2 - textSize/2;
textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + messageInputHeight/4 - (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -4221,7 +4409,7 @@ void GuiLoadStyle(const char *fileName)
if (fileDataSize > 0)
{
- unsigned char *fileData = (unsigned char *)RAYGUI_MALLOC(fileDataSize*sizeof(unsigned char));
+ unsigned char *fileData = (unsigned char *)RAYGUI_CALLOC(fileDataSize, sizeof(unsigned char));
fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile);
GuiLoadStyleFromMemory(fileData, fileDataSize);
@@ -4283,8 +4471,6 @@ void GuiLoadStyleDefault(void)
GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
GuiSetStyle(VALUEBOX, TEXT_PADDING, 0);
GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
- GuiSetStyle(SPINNER, TEXT_PADDING, 0);
- GuiSetStyle(SPINNER, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
GuiSetStyle(STATUSBAR, TEXT_PADDING, 8);
GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
@@ -4299,8 +4485,8 @@ void GuiLoadStyleDefault(void)
GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2);
GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16);
GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2);
- GuiSetStyle(SPINNER, SPIN_BUTTON_WIDTH, 24);
- GuiSetStyle(SPINNER, SPIN_BUTTON_SPACING, 2);
+ GuiSetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH, 24);
+ GuiSetStyle(VALUEBOX, SPINNER_BUTTON_SPACING, 2);
GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0);
GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0);
GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6);
@@ -4310,6 +4496,7 @@ void GuiLoadStyleDefault(void)
GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 12);
GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 28);
GuiSetStyle(LISTVIEW, LIST_ITEMS_SPACING, 2);
+ GuiSetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH, 1);
GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 12);
GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE);
GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 8);
@@ -4322,8 +4509,8 @@ void GuiLoadStyleDefault(void)
{
// Unload previous font texture
UnloadTexture(guiFont.texture);
- RL_FREE(guiFont.recs);
- RL_FREE(guiFont.glyphs);
+ RAYGUI_FREE(guiFont.recs);
+ RAYGUI_FREE(guiFont.glyphs);
guiFont.recs = NULL;
guiFont.glyphs = NULL;
@@ -4352,7 +4539,7 @@ const char *GuiIconText(int iconId, const char *text)
if (text != NULL)
{
memset(buffer, 0, 1024);
- sprintf(buffer, "#%03i#", iconId);
+ snprintf(buffer, 1024, "#%03i#", iconId);
for (int i = 5; i < 1024; i++)
{
@@ -4364,7 +4551,7 @@ const char *GuiIconText(int iconId, const char *text)
}
else
{
- sprintf(iconBuffer, "#%03i#", iconId);
+ snprintf(iconBuffer, 16, "#%03i#", iconId);
return iconBuffer;
}
@@ -4430,17 +4617,17 @@ char **GuiLoadIcons(const char *fileName, bool loadIconsName)
{
if (loadIconsName)
{
- guiIconsName = (char **)RAYGUI_MALLOC(iconCount*sizeof(char **));
+ guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *));
for (int i = 0; i < iconCount; i++)
{
- guiIconsName[i] = (char *)RAYGUI_MALLOC(RAYGUI_ICON_MAX_NAME_LENGTH);
+ guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char));
fread(guiIconsName[i], 1, RAYGUI_ICON_MAX_NAME_LENGTH, rgiFile);
}
}
else fseek(rgiFile, iconCount*RAYGUI_ICON_MAX_NAME_LENGTH, SEEK_CUR);
// Read icons data directly over internal icons array
- fread(guiIconsPtr, sizeof(unsigned int), iconCount*(iconSize*iconSize/32), rgiFile);
+ fread(guiIconsPtr, sizeof(unsigned int), (int)iconCount*((int)iconSize*(int)iconSize/32), rgiFile);
}
fclose(rgiFile);
@@ -4449,6 +4636,56 @@ char **GuiLoadIcons(const char *fileName, bool loadIconsName)
return guiIconsName;
}
+// Load icons from memory
+// WARNING: Binary files only
+char **GuiLoadIconsFromMemory(const unsigned char *fileData, int dataSize, bool loadIconsName)
+{
+ unsigned char *fileDataPtr = (unsigned char *)fileData;
+ char **guiIconsName = NULL;
+
+ char signature[5] = { 0 };
+ short version = 0;
+ short reserved = 0;
+ short iconCount = 0;
+ short iconSize = 0;
+
+ memcpy(signature, fileDataPtr, 4);
+ memcpy(&version, fileDataPtr + 4, sizeof(short));
+ memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short));
+ memcpy(&iconCount, fileDataPtr + 4 + 2 + 2, sizeof(short));
+ memcpy(&iconSize, fileDataPtr + 4 + 2 + 2 + 2, sizeof(short));
+ fileDataPtr += 12;
+
+ if ((signature[0] == 'r') &&
+ (signature[1] == 'G') &&
+ (signature[2] == 'I') &&
+ (signature[3] == ' '))
+ {
+ if (loadIconsName)
+ {
+ guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *));
+ for (int i = 0; i < iconCount; i++)
+ {
+ guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char));
+ memcpy(guiIconsName[i], fileDataPtr, RAYGUI_ICON_MAX_NAME_LENGTH);
+ fileDataPtr += RAYGUI_ICON_MAX_NAME_LENGTH;
+ }
+ }
+ else
+ {
+ // Skip icon name data if not required
+ fileDataPtr += iconCount*RAYGUI_ICON_MAX_NAME_LENGTH;
+ }
+
+ int iconDataSize = iconCount*((int)iconSize*(int)iconSize/32)*(int)sizeof(unsigned int);
+ guiIconsPtr = (unsigned int *)RAYGUI_CALLOC(iconDataSize, 1);
+
+ memcpy(guiIconsPtr, fileDataPtr, iconDataSize);
+ }
+
+ return guiIconsName;
+}
+
// Draw selected icon using rectangles pixel-by-pixel
void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color)
{
@@ -4476,12 +4713,73 @@ void GuiSetIconScale(int scale)
if (scale >= 1) guiIconScale = scale;
}
+// Get text width considering gui style and icon size (if required)
+int GuiGetTextWidth(const char *text)
+{
+ #if !defined(ICON_TEXT_PADDING)
+ #define ICON_TEXT_PADDING 4
+ #endif
+
+ Vector2 textSize = { 0 };
+ int textIconOffset = 0;
+
+ if ((text != NULL) && (text[0] != '\0'))
+ {
+ if (text[0] == '#')
+ {
+ for (int i = 1; (i < 5) && (text[i] != '\0'); i++)
+ {
+ if (text[i] == '#')
+ {
+ textIconOffset = i;
+ break;
+ }
+ }
+ }
+
+ text += textIconOffset;
+
+ // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly
+ float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+
+ // Custom MeasureText() implementation
+ if ((guiFont.texture.id > 0) && (text != NULL))
+ {
+ // Get size in bytes of text, considering end of line and line break
+ int size = 0;
+ for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++)
+ {
+ if ((text[i] != '\0') && (text[i] != '\n')) size++;
+ else break;
+ }
+
+ float scaleFactor = fontSize/(float)guiFont.baseSize;
+ textSize.y = (float)guiFont.baseSize*scaleFactor;
+ float glyphWidth = 0.0f;
+
+ for (int i = 0, codepointSize = 0; i < size; i += codepointSize)
+ {
+ int codepoint = GetCodepointNext(&text[i], &codepointSize);
+ int codepointIndex = GetGlyphIndex(guiFont, codepoint);
+
+ if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
+ else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
+
+ textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+ }
+ }
+
+ if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING);
+ }
+
+ return (int)textSize.x;
+}
+
#endif // !RAYGUI_NO_ICONS
//----------------------------------------------------------------------------------
// Module Internal Functions Definition
//----------------------------------------------------------------------------------
-
// Load style from memory
// WARNING: Binary files only
static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
@@ -4567,7 +4865,7 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
{
// Compressed font atlas image data (DEFLATE), it requires DecompressData()
int dataUncompSize = 0;
- unsigned char *compData = (unsigned char *)RAYGUI_MALLOC(fontImageCompSize);
+ unsigned char *compData = (unsigned char *)RAYGUI_CALLOC(fontImageCompSize, sizeof(unsigned char));
memcpy(compData, fileDataPtr, fontImageCompSize);
fileDataPtr += fontImageCompSize;
@@ -4581,7 +4879,7 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
else
{
// Font atlas image data is not compressed
- imFont.data = (unsigned char *)RAYGUI_MALLOC(fontImageUncompSize);
+ imFont.data = (unsigned char *)RAYGUI_CALLOC(fontImageUncompSize, sizeof(unsigned char));
memcpy(imFont.data, fileDataPtr, fontImageUncompSize);
fileDataPtr += fontImageUncompSize;
}
@@ -4609,7 +4907,7 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
if ((recsDataCompressedSize > 0) && (recsDataCompressedSize != recsDataSize))
{
// Recs data is compressed, uncompress it
- unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_MALLOC(recsDataCompressedSize);
+ unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_CALLOC(recsDataCompressedSize, sizeof(unsigned char));
memcpy(recsDataCompressed, fileDataPtr, recsDataCompressedSize);
fileDataPtr += recsDataCompressedSize;
@@ -4651,7 +4949,7 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
if ((glyphsDataCompressedSize > 0) && (glyphsDataCompressedSize != glyphsDataSize))
{
// Glyphs data is compressed, uncompress it
- unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_MALLOC(glyphsDataCompressedSize);
+ unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_CALLOC(glyphsDataCompressedSize, sizeof(unsigned char));
memcpy(glypsDataCompressed, fileDataPtr, glyphsDataCompressedSize);
fileDataPtr += glyphsDataCompressedSize;
@@ -4704,68 +5002,6 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
}
}
-// Gui get text width considering icon
-static int GetTextWidth(const char *text)
-{
- #if !defined(ICON_TEXT_PADDING)
- #define ICON_TEXT_PADDING 4
- #endif
-
- Vector2 textSize = { 0 };
- int textIconOffset = 0;
-
- if ((text != NULL) && (text[0] != '\0'))
- {
- if (text[0] == '#')
- {
- for (int i = 1; (i < 5) && (text[i] != '\0'); i++)
- {
- if (text[i] == '#')
- {
- textIconOffset = i;
- break;
- }
- }
- }
-
- text += textIconOffset;
-
- // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly
- float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
-
- // Custom MeasureText() implementation
- if ((guiFont.texture.id > 0) && (text != NULL))
- {
- // Get size in bytes of text, considering end of line and line break
- int size = 0;
- for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++)
- {
- if ((text[i] != '\0') && (text[i] != '\n')) size++;
- else break;
- }
-
- float scaleFactor = fontSize/(float)guiFont.baseSize;
- textSize.y = (float)guiFont.baseSize*scaleFactor;
- float glyphWidth = 0.0f;
-
- for (int i = 0, codepointSize = 0; i < size; i += codepointSize)
- {
- int codepoint = GetCodepointNext(&text[i], &codepointSize);
- int codepointIndex = GetGlyphIndex(guiFont, codepoint);
-
- if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
- else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
-
- textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
- }
- }
-
- if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING);
- }
-
- return (int)textSize.x;
-}
-
// Get text bounds considering control bounds
static Rectangle GetTextBounds(int control, Rectangle bounds)
{
@@ -4786,7 +5022,7 @@ static Rectangle GetTextBounds(int control, Rectangle bounds)
case SLIDER:
case CHECKBOX:
case VALUEBOX:
- case SPINNER:
+ case CONTROL11:
// TODO: More special cases (label on side): SLIDER, CHECKBOX, VALUEBOX, SPINNER
default:
{
@@ -4832,7 +5068,8 @@ static const char *GetTextIcon(const char *text, int *iconId)
}
// Get text divided into lines (by line-breaks '\n')
-const char **GetTextLines(const char *text, int *count)
+// WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator!
+static const char **GetTextLines(const char *text, int *count)
{
#define RAYGUI_MAX_TEXT_LINES 128
@@ -4842,25 +5079,18 @@ const char **GetTextLines(const char *text, int *count)
int textSize = (int)strlen(text);
lines[0] = text;
- int len = 0;
*count = 1;
- //int lineSize = 0; // Stores current line size, not returned
for (int i = 0, k = 0; (i < textSize) && (*count < RAYGUI_MAX_TEXT_LINES); i++)
{
if (text[i] == '\n')
{
- //lineSize = len;
k++;
- lines[k] = &text[i + 1]; // WARNING: next value is valid?
- len = 0;
+ lines[k] = &text[i + 1]; // WARNING: next value is valid?
*count += 1;
}
- else len++;
}
- //lines[*count - 1].size = len;
-
return lines;
}
@@ -4936,8 +5166,8 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C
float textBoundsWidthOffset = 0.0f;
// NOTE: We get text size after icon has been processed
- // WARNING: GetTextWidth() also processes text icon to get width! -> Really needed?
- int textSizeX = GetTextWidth(lines[i]);
+ // WARNING: GuiGetTextWidth() also processes text icon to get width! -> Really needed?
+ int textSizeX = GuiGetTextWidth(lines[i]);
// If text requires an icon, add size to measure
if (iconId >= 0)
@@ -5000,7 +5230,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C
float textOffsetX = 0.0f;
float glyphWidth = 0;
- int ellipsisWidth = GetTextWidth("...");
+ int ellipsisWidth = GuiGetTextWidth("...");
bool textOverflow = false;
for (int c = 0, codepointSize = 0; c < lineSize; c += codepointSize)
{
@@ -5144,13 +5374,13 @@ static void GuiTooltip(Rectangle controlRec)
if ((controlRec.x + textSize.x + 16) > GetScreenWidth()) controlRec.x -= (textSize.x + 16 - controlRec.width);
- GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.f }, NULL);
+ GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, NULL);
int textPadding = GuiGetStyle(LABEL, TEXT_PADDING);
int textAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
GuiSetStyle(LABEL, TEXT_PADDING, 0);
GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
- GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.f }, guiTooltipPtr);
+ GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, guiTooltipPtr);
GuiSetStyle(LABEL, TEXT_ALIGNMENT, textAlignment);
GuiSetStyle(LABEL, TEXT_PADDING, textPadding);
}
@@ -5204,7 +5434,7 @@ static const char **GuiTextSplit(const char *text, char delimiter, int *count, i
buffer[i] = '\0'; // Set an end of string at this point
counter++;
- if (counter > RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
+ if (counter >= RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
}
}
@@ -5526,10 +5756,10 @@ static Color GetColor(int hexValue)
{
Color color;
- color.r = (unsigned char)(hexValue >> 24) & 0xFF;
- color.g = (unsigned char)(hexValue >> 16) & 0xFF;
- color.b = (unsigned char)(hexValue >> 8) & 0xFF;
- color.a = (unsigned char)hexValue & 0xFF;
+ color.r = (unsigned char)(hexValue >> 24) & 0xff;
+ color.g = (unsigned char)(hexValue >> 16) & 0xff;
+ color.b = (unsigned char)(hexValue >> 8) & 0xff;
+ color.a = (unsigned char)hexValue & 0xff;
return color;
}
@@ -5562,7 +5792,7 @@ static const char *TextFormat(const char *text, ...)
va_list args;
va_start(args, text);
- vsprintf(buffer, text, args);
+ vsnprintf(buffer, RAYGUI_TEXTFORMAT_MAX_SIZE, text, args);
va_end(args);
return buffer;
@@ -5731,7 +5961,7 @@ static int GetCodepointNext(const char *text, int *codepointSize)
}
else if (0xe0 == (0xf0 & ptr[0]))
{
- // 3 byte UTF-8 codepoint */
+ // 3 byte UTF-8 codepoint
if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]);
*codepointSize = 3;
diff --git a/examples/shaders/resources/game_of_life/acorn.png b/examples/shaders/resources/game_of_life/acorn.png
new file mode 100644
index 000000000..58ea0b4d1
Binary files /dev/null and b/examples/shaders/resources/game_of_life/acorn.png differ
diff --git a/examples/shaders/resources/game_of_life/breeder.png b/examples/shaders/resources/game_of_life/breeder.png
new file mode 100644
index 000000000..88aa073d3
Binary files /dev/null and b/examples/shaders/resources/game_of_life/breeder.png differ
diff --git a/examples/shaders/resources/game_of_life/glider.png b/examples/shaders/resources/game_of_life/glider.png
new file mode 100644
index 000000000..921adb8ed
Binary files /dev/null and b/examples/shaders/resources/game_of_life/glider.png differ
diff --git a/examples/shaders/resources/game_of_life/glider_gun.png b/examples/shaders/resources/game_of_life/glider_gun.png
new file mode 100644
index 000000000..29b65d3de
Binary files /dev/null and b/examples/shaders/resources/game_of_life/glider_gun.png differ
diff --git a/examples/shaders/resources/game_of_life/oscillators.png b/examples/shaders/resources/game_of_life/oscillators.png
new file mode 100644
index 000000000..badd6a92d
Binary files /dev/null and b/examples/shaders/resources/game_of_life/oscillators.png differ
diff --git a/examples/shaders/resources/game_of_life/puffer_train.png b/examples/shaders/resources/game_of_life/puffer_train.png
new file mode 100644
index 000000000..8d77219b0
Binary files /dev/null and b/examples/shaders/resources/game_of_life/puffer_train.png differ
diff --git a/examples/shaders/resources/game_of_life/r_pentomino.png b/examples/shaders/resources/game_of_life/r_pentomino.png
new file mode 100644
index 000000000..1707f3c12
Binary files /dev/null and b/examples/shaders/resources/game_of_life/r_pentomino.png differ
diff --git a/examples/shaders/resources/game_of_life/spaceships.png b/examples/shaders/resources/game_of_life/spaceships.png
new file mode 100644
index 000000000..867f6fea3
Binary files /dev/null and b/examples/shaders/resources/game_of_life/spaceships.png differ
diff --git a/examples/shaders/resources/game_of_life/still_lifes.png b/examples/shaders/resources/game_of_life/still_lifes.png
new file mode 100644
index 000000000..ac184ac49
Binary files /dev/null and b/examples/shaders/resources/game_of_life/still_lifes.png differ
diff --git a/examples/shaders/resources/shaders/glsl100/game_of_life.fs b/examples/shaders/resources/shaders/glsl100/game_of_life.fs
new file mode 100644
index 000000000..70c12ac2c
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl100/game_of_life.fs
@@ -0,0 +1,44 @@
+#version 100
+
+precision highp float;
+
+// Input vertex attributes (from vertex shader)
+varying vec2 fragTexCoord;
+varying vec4 fragColor;
+
+// Input uniform values
+uniform sampler2D texture0;
+uniform vec4 colDiffuse;
+
+// Input size in pixels of the textures
+uniform vec2 resolution;
+
+void main()
+{
+ // Size of one pixel in texture coordinates (from 0.0 to 1.0)
+ float x = 1.0/resolution.x;
+ float y = 1.0/resolution.y;
+
+ // Status of the current cell (1 = alive, 0 = dead)
+ int origValue = (texture2D(texture0, fragTexCoord).r < 0.1)? 1 : 0;
+
+ // Sum of alive neighbors
+ int sumValue = (texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y)).r < 0.1)? 1 : 0; // Top-left
+ sumValue += (texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y )).r < 0.1)? 1 : 0; // Top
+ sumValue += (texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y)).r < 0.1)? 1 : 0; // Top-right
+
+ sumValue += (texture2D(texture0, vec2(fragTexCoord.x, fragTexCoord.y - y)).r < 0.1)? 1 : 0; // Left
+ sumValue += (texture2D(texture0, vec2(fragTexCoord.x, fragTexCoord.y + y)).r < 0.1)? 1 : 0; // Right
+
+ sumValue += (texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y)).r < 0.1)? 1 : 0; // Bottom-left
+ sumValue += (texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y )).r < 0.1)? 1 : 0; // Bottom
+ sumValue += (texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y)).r < 0.1)? 1 : 0; // Bottom-right
+
+ // Game of life rules:
+ // Current cell remains alive when 2 or 3 neighbors are alive, dies otherwise
+ // Current cell goes from dead to alive when exactly 3 neighbors are alive
+ if ((origValue == 1 && sumValue == 2) || sumValue == 3)
+ gl_FragColor = vec4(0.0, 0.0, 0.0, 255.0); // Alive: draw the pixel black
+ else
+ gl_FragColor = fragColor; // Dead: draw the pixel with the background color, RAYWHITE
+}
diff --git a/examples/shaders/resources/shaders/glsl100/hybrid_raster.fs b/examples/shaders/resources/shaders/glsl100/hybrid_raster.fs
index 35bc75d30..9658b3819 100644
--- a/examples/shaders/resources/shaders/glsl100/hybrid_raster.fs
+++ b/examples/shaders/resources/shaders/glsl100/hybrid_raster.fs
@@ -1,6 +1,7 @@
#version 100
-#extension GL_EXT_frag_depth : enable // Extension required for writing depth
+#extension GL_EXT_frag_depth : enable // Extension required for writing depth
+
precision mediump float; // Precision required for OpenGL ES2 (WebGL)
varying vec2 fragTexCoord;
diff --git a/examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs b/examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs
index 44233b3bb..8f9fa0907 100644
--- a/examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs
+++ b/examples/shaders/resources/shaders/glsl100/hybrid_raymarch.fs
@@ -2,6 +2,9 @@
#extension GL_EXT_frag_depth : enable //Extension required for writing depth
#extension GL_OES_standard_derivatives : enable //Extension used for fwidth()
+
+#define ZERO 0
+
precision mediump float; // Precision required for OpenGL ES2 (WebGL)
// Input vertex attributes (from vertex shader)
@@ -17,8 +20,6 @@ uniform vec3 camPos;
uniform vec3 camDir;
uniform vec2 screenCenter;
-#define ZERO 0
-
// SRC: https://learnopengl.com/Advanced-OpenGL/Depth-testing
float CalcDepth(in vec3 rd, in float Idist)
{
@@ -128,7 +129,7 @@ float calcSoftshadow(in vec3 ro, in vec3 rd, in float mint, in float tmax)
float res = 1.0;
float t = mint;
- for (int i=ZERO; i<24; i++)
+ for (int i = ZERO; i < 24; i++)
{
float h = map(ro + rd*t).x;
float s = clamp(8.0*h/t,0.0,1.0);
@@ -156,7 +157,7 @@ float calcAO(in vec3 pos, in vec3 nor)
{
float occ = 0.0;
float sca = 1.0;
- for (int i=ZERO; i<5; i++)
+ for (int i = ZERO; i < 5; i++)
{
float h = 0.01 + 0.12*float(i)/4.0;
float d = map(pos + h*nor).x;
@@ -257,7 +258,8 @@ vec4 render(in vec3 ro, in vec3 rd)
return vec4(vec3(clamp(col,0.0,1.0)),t);
}
-vec3 CalcRayDir(vec2 nCoord){
+vec3 CalcRayDir(vec2 nCoord)
+{
vec3 horizontal = normalize(cross(camDir,vec3(.0 , 1.0, .0)));
vec3 vertical = normalize(cross(horizontal,camDir));
return normalize(camDir + horizontal*nCoord.x + vertical*nCoord.y);
@@ -287,6 +289,7 @@ void main()
color = res.xyz;
depth = CalcDepth(rd,res.w);
}
+
gl_FragColor = vec4(color , 1.0);
gl_FragDepthEXT = depth;
}
\ No newline at end of file
diff --git a/examples/shaders/resources/shaders/glsl100/raymarching.fs b/examples/shaders/resources/shaders/glsl100/raymarching.fs
index ce9a2faca..58cd41059 100644
--- a/examples/shaders/resources/shaders/glsl100/raymarching.fs
+++ b/examples/shaders/resources/shaders/glsl100/raymarching.fs
@@ -1,9 +1,9 @@
#version 100
-precision mediump float;
-
#extension GL_OES_standard_derivatives : enable
+precision mediump float;
+
// Input vertex attributes (from vertex shader)
varying vec2 fragTexCoord;
varying vec4 fragColor;
diff --git a/examples/shaders/resources/shaders/glsl120/game_of_life.fs b/examples/shaders/resources/shaders/glsl120/game_of_life.fs
new file mode 100644
index 000000000..611f961bc
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl120/game_of_life.fs
@@ -0,0 +1,42 @@
+#version 120
+
+// Input vertex attributes (from vertex shader)
+varying vec2 fragTexCoord;
+varying vec4 fragColor;
+
+// Input uniform values
+uniform sampler2D texture0;
+uniform vec4 colDiffuse;
+
+// Input size in pixels of the textures
+uniform vec2 resolution;
+
+void main()
+{
+ // Size of one pixel in texture coordinates (from 0.0 to 1.0)
+ float x = 1.0/resolution.x;
+ float y = 1.0/resolution.y;
+
+ // Status of the current cell (1 = alive, 0 = dead)
+ int origValue = (texture2D(texture0, fragTexCoord).r < 0.1)? 1 : 0;
+
+ // Sum of alive neighbors
+ int sumValue = (texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y)).r < 0.1)? 1 : 0; // Top-left
+ sumValue += (texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y )).r < 0.1)? 1 : 0; // Top
+ sumValue += (texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y)).r < 0.1)? 1 : 0; // Top-right
+
+ sumValue += (texture2D(texture0, vec2(fragTexCoord.x, fragTexCoord.y - y)).r < 0.1)? 1 : 0; // Left
+ sumValue += (texture2D(texture0, vec2(fragTexCoord.x, fragTexCoord.y + y)).r < 0.1)? 1 : 0; // Right
+
+ sumValue += (texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y)).r < 0.1)? 1 : 0; // Bottom-left
+ sumValue += (texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y )).r < 0.1)? 1 : 0; // Bottom
+ sumValue += (texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y)).r < 0.1)? 1 : 0; // Bottom-right
+
+ // Game of life rules:
+ // Current cell remains alive when 2 or 3 neighbors are alive, dies otherwise
+ // Current cell goes from dead to alive when exactly 3 neighbors are alive
+ if (((origValue == 1) && (sumValue == 2)) || sumValue == 3)
+ gl_FragColor = vec4(0.0, 0.0, 0.0, 255.0); // Alive: draw the pixel black
+ else
+ gl_FragColor = fragColor; // Dead: draw the pixel with the background color, RAYWHITE
+}
diff --git a/examples/shaders/resources/shaders/glsl330/game_of_life.fs b/examples/shaders/resources/shaders/glsl330/game_of_life.fs
new file mode 100644
index 000000000..cc80861d6
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl330/game_of_life.fs
@@ -0,0 +1,45 @@
+#version 330
+
+// Input vertex attributes (from vertex shader)
+in vec2 fragTexCoord;
+in vec4 fragColor;
+
+// Input uniform values
+uniform sampler2D texture0;
+uniform vec4 colDiffuse;
+
+// Output fragment color
+out vec4 finalColor;
+
+// Input size in pixels of the textures
+uniform vec2 resolution;
+
+void main()
+{
+ // Size of one pixel in texture coordinates (from 0.0 to 1.0)
+ float x = 1.0/resolution.x;
+ float y = 1.0/resolution.y;
+
+ // Status of the current cell (1 = alive, 0 = dead)
+ int origValue = (texture(texture0, fragTexCoord).r < 0.1)? 1 : 0;
+
+ // Sum of alive neighbors
+ int sumValue = (texture(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y)).r < 0.1)? 1 : 0; // Top-left
+ sumValue += (texture(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y )).r < 0.1)? 1 : 0; // Top
+ sumValue += (texture(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y)).r < 0.1)? 1 : 0; // Top-right
+
+ sumValue += (texture(texture0, vec2(fragTexCoord.x, fragTexCoord.y - y)).r < 0.1)? 1 : 0; // Left
+ sumValue += (texture(texture0, vec2(fragTexCoord.x, fragTexCoord.y + y)).r < 0.1)? 1 : 0; // Right
+
+ sumValue += (texture(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y)).r < 0.1)? 1 : 0; // Bottom-left
+ sumValue += (texture(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y )).r < 0.1)? 1 : 0; // Bottom
+ sumValue += (texture(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y)).r < 0.1)? 1 : 0; // Bottom-right
+
+ // Game of life rules:
+ // Current cell remains alive when 2 or 3 neighbors are alive, dies otherwise
+ // Current cell goes from dead to alive when exactly 3 neighbors are alive
+ if (((origValue == 1) && (sumValue == 2)) || sumValue == 3)
+ finalColor = vec4(0.0, 0.0, 0.0, 255.0); // Alive: draw the pixel black
+ else
+ finalColor = fragColor; // Dead: draw the pixel with the background color, RAYWHITE
+}
diff --git a/examples/shaders/resources/shaders/glsl330/hybrid_raster.fs b/examples/shaders/resources/shaders/glsl330/hybrid_raster.fs
index 12409d137..0b94dbdef 100644
--- a/examples/shaders/resources/shaders/glsl330/hybrid_raster.fs
+++ b/examples/shaders/resources/shaders/glsl330/hybrid_raster.fs
@@ -9,7 +9,7 @@ uniform sampler2D texture0;
uniform vec4 colDiffuse;
// Output fragment color
-//out vec4 finalColor;
+out vec4 finalColor;
// NOTE: Add your custom variables here
@@ -17,6 +17,6 @@ void main()
{
vec4 texelColor = texture(texture0, fragTexCoord);
- gl_FragColor = texelColor*colDiffuse*fragColor;
- gl_FragDepth = gl_FragCoord.z;
+ finalColor = texelColor*colDiffuse*fragColor;
+ gl_FragDepth = finalColor.z;
}
\ No newline at end of file
diff --git a/examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs b/examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs
index 2edb625ad..f1fafc640 100644
--- a/examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs
+++ b/examples/shaders/resources/shaders/glsl330/hybrid_raymarch.fs
@@ -1,5 +1,7 @@
# version 330
+#define ZERO 0
+
// Input vertex attributes (from vertex shader)
in vec2 fragTexCoord;
in vec4 fragColor;
@@ -13,10 +15,12 @@ uniform vec3 camPos;
uniform vec3 camDir;
uniform vec2 screenCenter;
-#define ZERO 0
+// Output fragment color
+out vec4 finalColor;
// https://learnopengl.com/Advanced-OpenGL/Depth-testing
-float CalcDepth(in vec3 rd, in float Idist){
+float CalcDepth(in vec3 rd, in float Idist)
+{
float local_z = dot(normalize(camDir),rd)*Idist;
return (1.0/(local_z) - 1.0/0.01)/(1.0/1000.0 -1.0/0.01);
}
@@ -26,15 +30,13 @@ float sdHorseshoe(in vec3 p, in vec2 c, in float r, in float le, vec2 w)
{
p.x = abs(p.x);
float l = length(p.xy);
- p.xy = mat2(-c.x, c.y,
- c.y, c.x)*p.xy;
- p.xy = vec2((p.y>0.0 || p.x>0.0)?p.x:l*sign(-c.x),
- (p.x>0.0)?p.y:l);
- p.xy = vec2(p.x,abs(p.y-r))-vec2(le,0.0);
+ p.xy = mat2(-c.x, c.y, c.y, c.x)*p.xy;
+ p.xy = vec2(((p.y > 0.0) || (p.x > 0.0))? p.x : l*sign(-c.x), (p.x>0.0)? p.y : l);
+ p.xy = vec2(p.x, abs(p.y - r)) - vec2(le, 0.0);
- vec2 q = vec2(length(max(p.xy,0.0)) + min(0.0,max(p.x,p.y)),p.z);
+ vec2 q = vec2(length(max(p.xy, 0.0)) + min(0.0, max(p.x, p.y)), p.z);
vec2 d = abs(q) - w;
- return min(max(d.x,d.y),0.0) + length(max(d,0.0));
+ return min(max(d.x, d.y), 0.0) + length(max(d, 0.0));
}
// r = sphere's radius
@@ -44,17 +46,16 @@ float sdSixWayCutHollowSphere(vec3 p, float r, float h, float t)
{
// Six way symetry Transformation
vec3 ap = abs(p);
- if (ap.x < max(ap.y, ap.z)){
+ if (ap.x < max(ap.y, ap.z))
+ {
if (ap.y < ap.z) ap.xz = ap.zx;
else ap.xy = ap.yx;
}
vec2 q = vec2(length(ap.yz), ap.x);
-
float w = sqrt(r*r-h*h);
- return ((h*q.x0.0)
+ if (tp1 > 0.0)
{
tmax = min(tmax, tp1);
res = vec2(tp1, 1.0);
}
float t = tmin;
- for (int i=0; i<70 ; i++)
+ for (int i = 0; i < 70 ; i++)
{
- if (t>tmax) break;
- vec2 h = map(ro+rd*t);
- if (abs(h.x)<(0.0001*t))
+ if (t > tmax) break;
+ vec2 h = map(ro + rd*t);
+ if (abs(h.x )< (0.0001*t))
{
- res = vec2(t,h.y);
+ res = vec2(t, h.y);
break;
}
t += h.x;
@@ -111,28 +115,28 @@ vec2 raycast(in vec3 ro, in vec3 rd){
return res;
}
-
// https://iquilezles.org/articles/rmshadows
float calcSoftshadow(in vec3 ro, in vec3 rd, in float mint, in float tmax)
{
// bounding volume
- float tp = (0.8-ro.y)/rd.y; if (tp>0.0) tmax = min(tmax, tp);
+ float tp = (0.8 - ro.y)/rd.y; if (tp > 0.0) tmax = min(tmax, tp);
float res = 1.0;
float t = mint;
- for (int i=ZERO; i<24; i++)
+ for (int i = ZERO; i < 24; i++)
{
float h = map(ro + rd*t).x;
- float s = clamp(8.0*h/t,0.0,1.0);
+ float s = clamp(8.0*h/t, 0.0, 1.0);
res = min(res, s);
t += clamp(h, 0.01, 0.2);
- if (res<0.004 || t>tmax) break;
+ if ((res < 0.004) || (t > tmax)) break;
}
+
res = clamp(res, 0.0, 1.0);
+
return res*res*(3.0-2.0*res);
}
-
// https://iquilezles.org/articles/normalsSDF
vec3 calcNormal(in vec3 pos)
{
@@ -148,7 +152,7 @@ float calcAO(in vec3 pos, in vec3 nor)
{
float occ = 0.0;
float sca = 1.0;
- for (int i=ZERO; i<5; i++)
+ for (int i = ZERO; i < 5; i++)
{
float h = 0.01 + 0.12*float(i)/4.0;
float d = map(pos + h*nor).x;
@@ -156,6 +160,7 @@ float calcAO(in vec3 pos, in vec3 nor)
sca *= 0.95;
if (occ>0.35) break;
}
+
return clamp(1.0 - 3.0*occ, 0.0, 1.0)*(0.5+0.5*nor.y);
}
@@ -165,9 +170,9 @@ float checkersGradBox(in vec2 p)
// filter kernel
vec2 w = fwidth(p) + 0.001;
// analytical integral (box filter)
- vec2 i = 2.0*(abs(fract((p-0.5*w)*0.5)-0.5)-abs(fract((p+0.5*w)*0.5)-0.5))/w;
+ vec2 i = 2.0*(abs(fract((p - 0.5*w)*0.5)-0.5) - abs(fract((p + 0.5*w)*0.5) - 0.5))/w;
// xor pattern
- return 0.5 - 0.5*i.x*i.y;
+ return (0.5 - 0.5*i.x*i.y);
}
// https://www.shadertoy.com/view/tdS3DG
@@ -180,7 +185,7 @@ vec4 render(in vec3 ro, in vec3 rd)
vec2 res = raycast(ro,rd);
float t = res.x;
float m = res.y;
- if (m>-0.5)
+ if (m > -0.5)
{
vec3 pos = ro + t*rd;
vec3 nor = (m<1.5) ? vec3(0.0,1.0,0.0) : calcNormal(pos);
@@ -190,7 +195,7 @@ vec4 render(in vec3 ro, in vec3 rd)
col = 0.2 + 0.2*sin(m*2.0 + vec3(0.0,1.0,2.0));
float ks = 1.0;
- if (m<1.5)
+ if (m < 1.5)
{
float f = checkersGradBox(3.0*pos.xz);
col = 0.15 + f*vec3(0.05);
@@ -207,14 +212,14 @@ vec4 render(in vec3 ro, in vec3 rd)
vec3 lig = normalize(vec3(-0.5, 0.4, -0.6));
vec3 hal = normalize(lig-rd);
float dif = clamp(dot(nor, lig), 0.0, 1.0);
- //if (dif>0.0001)
+ //if (dif>0.0001)
dif *= calcSoftshadow(pos, lig, 0.02, 2.5);
float spe = pow(clamp(dot(nor, hal), 0.0, 1.0),16.0);
spe *= dif;
spe *= 0.04+0.96*pow(clamp(1.0-dot(hal,lig),0.0,1.0),5.0);
//spe *= 0.04+0.96*pow(clamp(1.0-sqrt(0.5*(1.0-dot(rd,lig))),0.0,1.0),5.0);
lin += col*2.20*dif*vec3(1.30,1.00,0.70);
- lin += 5.00*spe*vec3(1.30,1.00,0.70)*ks;
+ lin += 5.00*spe*vec3(1.30,1.00,0.70)*ks;
}
// sky
{
@@ -249,7 +254,8 @@ vec4 render(in vec3 ro, in vec3 rd)
return vec4(vec3(clamp(col,0.0,1.0)),t);
}
-vec3 CalcRayDir(vec2 nCoord){
+vec3 CalcRayDir(vec2 nCoord)
+{
vec3 horizontal = normalize(cross(camDir,vec3(.0 , 1.0, .0)));
vec3 vertical = normalize(cross(horizontal,camDir));
return normalize(camDir + horizontal*nCoord.x + vertical*nCoord.y);
@@ -279,6 +285,7 @@ void main()
color = res.xyz;
depth = CalcDepth(rd,res.w);
}
- gl_FragColor = vec4(color , 1.0);
+
+ finalColor = vec4(color , 1.0);
gl_FragDepth = depth;
}
\ No newline at end of file
diff --git a/examples/shaders/shaders_ascii_rendering.c b/examples/shaders/shaders_ascii_rendering.c
index 523b381b2..d3302bf53 100644
--- a/examples/shaders/shaders_ascii_rendering.c
+++ b/examples/shaders/shaders_ascii_rendering.c
@@ -87,15 +87,15 @@ int main(void)
DrawTexture(fudesumi, 500, -30, WHITE);
DrawTextureV(raysan, circlePos, WHITE);
EndTextureMode();
-
+
BeginDrawing();
ClearBackground(RAYWHITE);
BeginShaderMode(shader);
// Draw the scene texture (that we rendered earlier) to the screen
// The shader will process every pixel of this texture
- DrawTextureRec(target.texture,
- (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height },
+ DrawTextureRec(target.texture,
+ (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height },
(Vector2){ 0, 0 }, WHITE);
EndShaderMode();
diff --git a/examples/shaders/shaders_basic_pbr.c b/examples/shaders/shaders_basic_pbr.c
index 6fb15a607..dd5f871da 100644
--- a/examples/shaders/shaders_basic_pbr.c
+++ b/examples/shaders/shaders_basic_pbr.c
@@ -77,7 +77,7 @@ static void UpdateLight(Shader shader, Light light);
//----------------------------------------------------------------------------------
// Program main entry point
//----------------------------------------------------------------------------------
-int main()
+int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
@@ -242,7 +242,7 @@ int main()
SetShaderValue(shader, emissiveColorLoc, &carEmissiveColor, SHADER_UNIFORM_VEC4);
float emissiveIntensity = 0.01f;
SetShaderValue(shader, emissiveIntensityLoc, &emissiveIntensity, SHADER_UNIFORM_FLOAT);
-
+
// Set old car metallic and roughness values
SetShaderValue(shader, metallicValueLoc, &car.materials[0].maps[MATERIAL_MAP_METALNESS].value, SHADER_UNIFORM_FLOAT);
SetShaderValue(shader, roughnessValueLoc, &car.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value, SHADER_UNIFORM_FLOAT);
@@ -252,11 +252,11 @@ int main()
// Draw spheres to show the lights positions
for (int i = 0; i < MAX_LIGHTS; i++)
{
- Color lightColor = (Color){
+ Color lightColor = (Color){
(unsigned char)(lights[i].color[0]*255),
- (unsigned char)(lights[i].color[1] * 255),
- (unsigned char)(lights[i].color[2] * 255),
- (unsigned char)(lights[i].color[3] * 255) };
+ (unsigned char)(lights[i].color[1]*255),
+ (unsigned char)(lights[i].color[2]*255),
+ (unsigned char)(lights[i].color[3]*255) };
if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lightColor);
else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(lightColor, 0.3f));
diff --git a/examples/shaders/shaders_color_correction.c b/examples/shaders/shaders_color_correction.c
index 3abf29840..826baecc7 100644
--- a/examples/shaders/shaders_color_correction.c
+++ b/examples/shaders/shaders_color_correction.c
@@ -138,7 +138,7 @@ int main(void)
// De-Initialization
//--------------------------------------------------------------------------------------
- for (int i = 0; i < MAX_TEXTURES; ++i) UnloadTexture(texture[i]);
+ for (int i = 0; i < MAX_TEXTURES; i++) UnloadTexture(texture[i]);
UnloadShader(shdrColorCorrection);
CloseWindow(); // Close window and OpenGL context
diff --git a/examples/shaders/shaders_game_of_life.c b/examples/shaders/shaders_game_of_life.c
new file mode 100644
index 000000000..9b9242a0d
--- /dev/null
+++ b/examples/shaders/shaders_game_of_life.c
@@ -0,0 +1,353 @@
+/*******************************************************************************************
+*
+* raylib [shaders] example - game of life
+*
+* Example complexity rating: [★★★☆] 3/4
+*
+* NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
+* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
+*
+* Example originally created with raylib 5.6, last time updated with raylib 5.6
+*
+* Example contributed by Jordi Santonja (@JordSant) 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 Jordi Santonja (@JordSant)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h" // Required for GUI controls
+
+#if defined(PLATFORM_DESKTOP)
+ #define GLSL_VERSION 330
+#else // PLATFORM_ANDROID, PLATFORM_WEB
+ #define GLSL_VERSION 100
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Interaction mode
+typedef enum {
+ MODE_RUN = 0,
+ MODE_PAUSE,
+ MODE_DRAW,
+} InteractionMode;
+
+// Struct to store example preset patterns
+typedef struct {
+ char *name;
+ Vector2 position;
+} PresetPattern;
+
+//----------------------------------------------------------------------------------
+// Functions declaration
+//----------------------------------------------------------------------------------
+void FreeImageToDraw(Image **imageToDraw);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [shaders] example - game of life");
+
+ const int menuWidth = 100;
+ const int windowWidth = screenWidth - menuWidth;
+ const int windowHeight = screenHeight;
+
+ const int worldWidth = 2048;
+ const int worldHeight = 2048;
+
+ const int randomTiles = 8; // Random preset: divide the world to compute random points in each tile
+
+ const Rectangle worldRectSource = { 0, 0, (float)worldWidth, (float)-worldHeight };
+ const Rectangle worldRectDest = { 0, 0, (float)worldWidth, (float)worldHeight };
+ const Rectangle textureOnScreen = { 0, 0, (float)windowWidth, (float)windowHeight };
+
+ const PresetPattern presetPatterns[] = {
+ { "Glider", { 0.5f, 0.5f } }, { "R-pentomino", { 0.5f, 0.5f } }, { "Acorn", { 0.5f,0.5f } },
+ { "Spaceships", { 0.1f, 0.5f } }, { "Still lifes", { 0.5f, 0.5f } }, { "Oscillators", { 0.5f, 0.5f } },
+ { "Puffer train", { 0.1f, 0.5f } }, { "Glider Gun", { 0.2f, 0.2f } }, { "Breeder", { 0.1f, 0.5f } },
+ { "Random", { 0.5f, 0.5f } }
+ };
+
+ const int numberOfPresets = sizeof(presetPatterns)/sizeof(presetPatterns[0]);
+
+ int zoom = 1;
+ float offsetX = (worldWidth - windowWidth)/2.0f; // Centered on window
+ float offsetY = (worldHeight - windowHeight)/2.0f; // Centered on window
+ int framesPerStep = 1;
+ int frame = 0;
+
+ int preset = -1; // No button pressed for preset
+ int mode = MODE_RUN; // Starting mode: running
+ bool buttonZoomIn = false; // Button states: false not pressed
+ bool buttonZomOut = false;
+ bool buttonFaster = false;
+ bool buttonSlower = false;
+
+ // Load shader
+ Shader shdrGameOfLife = LoadShader(0, TextFormat("resources/shaders/glsl%i/game_of_life.fs", GLSL_VERSION));
+
+ // Set shader uniform size of the world
+ int resolutionLoc = GetShaderLocation(shdrGameOfLife, "resolution");
+ const float resolution[2] = { (float)worldWidth, (float)worldHeight };
+ SetShaderValue(shdrGameOfLife, resolutionLoc, resolution, SHADER_UNIFORM_VEC2);
+
+ // Define two textures: the current world and the previous world
+ RenderTexture2D world1 = LoadRenderTexture(worldWidth, worldHeight);
+ RenderTexture2D world2 = LoadRenderTexture(worldWidth, worldHeight);
+ BeginTextureMode(world2);
+ ClearBackground(RAYWHITE);
+ EndTextureMode();
+
+ Image startPattern = LoadImage("resources/game_of_life/r_pentomino.png");
+ UpdateTextureRec(world2.texture, (Rectangle){ worldWidth/2.0f, worldHeight/2.0f, (float)(startPattern.width), (float)(startPattern.height) }, startPattern.data);
+ UnloadImage(startPattern);
+
+ // Pointers to the two textures, to be swapped
+ RenderTexture2D *currentWorld = &world2;
+ RenderTexture2D *previousWorld = &world1;
+
+ // Image to be used in DRAW mode, to be changed with mouse input
+ Image *imageToDraw = NULL;
+
+ SetTargetFPS(60); // Set at 60 frames-per-second
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ frame++;
+
+ // Change zoom: both by buttons or by mouse wheel
+ float mouseWheelMove = GetMouseWheelMove();
+ if (buttonZoomIn || (buttonZomOut && (zoom > 1)) || (mouseWheelMove != 0.0f))
+ {
+ FreeImageToDraw(&imageToDraw); // Zoom change: free the image to draw to be recreated again
+
+ const float centerX = offsetX + (windowWidth/2.0f)/zoom;
+ const float centerY = offsetY + (windowHeight/2.0f)/zoom;
+ if (buttonZoomIn || (mouseWheelMove > 0.0f)) zoom *= 2;
+ if ((buttonZomOut || (mouseWheelMove < 0.0f)) && (zoom > 1)) zoom /= 2;
+ offsetX = centerX - (windowWidth/2.0f)/zoom;
+ offsetY = centerY - (windowHeight/2.0f)/zoom;
+ }
+
+ // Change speed: number of frames per step
+ if (buttonFaster && framesPerStep > 1) framesPerStep--;
+ if (buttonSlower) framesPerStep++;
+
+ // Mouse management
+ if ((mode == MODE_RUN) || (mode == MODE_PAUSE))
+ {
+ FreeImageToDraw(&imageToDraw); // Free the image to draw: no longer needed in these modes
+
+ // Pan with mouse left button
+ static Vector2 previousMousePosition = { 0.0f, 0.0f };
+ const Vector2 mousePosition = GetMousePosition();
+ if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) && (mousePosition.x < windowWidth))
+ {
+ offsetX -= (mousePosition.x - previousMousePosition.x)/zoom;
+ offsetY -= (mousePosition.y - previousMousePosition.y)/zoom;
+ }
+ previousMousePosition = mousePosition;
+ }
+ else // MODE_DRAW
+ {
+ const float offsetDecimalX = offsetX - floorf(offsetX);
+ const float offsetDecimalY = offsetY - floorf(offsetY);
+ int sizeInWorldX = (int)(ceilf((float)(windowWidth + offsetDecimalX*zoom)/zoom));
+ int sizeInWorldY = (int)(ceilf((float)(windowHeight + offsetDecimalY*zoom)/zoom));
+ if (offsetX + sizeInWorldX >= worldWidth) sizeInWorldX = worldWidth - (int)floorf(offsetX);
+ if (offsetY + sizeInWorldY >= worldHeight) sizeInWorldY = worldHeight - (int)floorf(offsetY);
+
+ // Create image to draw if not created yet
+ if (imageToDraw == NULL)
+ {
+ RenderTexture2D worldOnScreen = LoadRenderTexture(sizeInWorldX, sizeInWorldY);
+ BeginTextureMode(worldOnScreen);
+ DrawTexturePro(currentWorld->texture, (Rectangle) { floorf(offsetX), floorf(offsetY), (float)(sizeInWorldX), -(float)(sizeInWorldY) },
+ (Rectangle) { 0, 0, (float)(sizeInWorldX), (float)(sizeInWorldY) }, (Vector2) { 0, 0 }, 0.0f, WHITE);
+ EndTextureMode();
+ imageToDraw = (Image*)RL_MALLOC(sizeof(Image));
+ *imageToDraw = LoadImageFromTexture(worldOnScreen.texture);
+
+ UnloadRenderTexture(worldOnScreen);
+ }
+
+ const Vector2 mousePosition = GetMousePosition();
+ static int firstColor = -1;
+ if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) && (mousePosition.x < windowWidth))
+ {
+ int mouseX = (int)(mousePosition.x + offsetDecimalX*zoom)/zoom;
+ int mouseY = (int)(mousePosition.y + offsetDecimalY*zoom)/zoom;
+ if (mouseX >= sizeInWorldX) mouseX = sizeInWorldX - 1;
+ if (mouseY >= sizeInWorldY) mouseY = sizeInWorldY - 1;
+ if (firstColor == -1) firstColor = (GetImageColor(*imageToDraw, mouseX, mouseY).r < 5)? 0 : 1;
+ const int prevColor = (GetImageColor(*imageToDraw, mouseX, mouseY).r < 5)? 0 : 1;
+
+ ImageDrawPixel(imageToDraw, mouseX, mouseY, (firstColor) ? BLACK : RAYWHITE);
+
+ if (prevColor != firstColor) UpdateTextureRec(currentWorld->texture, (Rectangle){ floorf(offsetX), floorf(offsetY), (float)(sizeInWorldX), (float)(sizeInWorldY) }, imageToDraw->data);
+ }
+ else firstColor = -1;
+ }
+
+ // Load selected preset
+ if (preset >= 0)
+ {
+ Image pattern;
+ if (preset < numberOfPresets - 1) // Preset with pattern image lo load
+ {
+ switch (preset)
+ {
+ case 0: pattern = LoadImage("resources/game_of_life/glider.png"); break;
+ case 1: pattern = LoadImage("resources/game_of_life/r_pentomino.png"); break;
+ case 2: pattern = LoadImage("resources/game_of_life/acorn.png"); break;
+ case 3: pattern = LoadImage("resources/game_of_life/spaceships.png"); break;
+ case 4: pattern = LoadImage("resources/game_of_life/still_lifes.png"); break;
+ case 5: pattern = LoadImage("resources/game_of_life/oscillators.png"); break;
+ case 6: pattern = LoadImage("resources/game_of_life/puffer_train.png"); break;
+ case 7: pattern = LoadImage("resources/game_of_life/glider_gun.png"); break;
+ case 8: pattern = LoadImage("resources/game_of_life/breeder.png"); break;
+ }
+ BeginTextureMode(*currentWorld);
+ ClearBackground(RAYWHITE);
+ EndTextureMode();
+
+ UpdateTextureRec(currentWorld->texture, (Rectangle){ worldWidth*presetPatterns[preset].position.x - pattern.width/2.0f,
+ worldHeight*presetPatterns[preset].position.y - pattern.height/2.0f,
+ (float)(pattern.width), (float)(pattern.height) }, pattern.data);
+ }
+ else // Last preset: Random values
+ {
+ pattern = GenImageColor(worldWidth/randomTiles, worldHeight/randomTiles, RAYWHITE);
+ for (int i = 0; i < randomTiles; i++)
+ {
+ for (int j = 0; j < randomTiles; j++)
+ {
+ ImageClearBackground(&pattern, RAYWHITE);
+ for (int x = 0; x < pattern.width; x++)
+ {
+ for (int y = 0; y < pattern.height; y++)
+ {
+ if (GetRandomValue(0, 100) < 15) ImageDrawPixel(&pattern, x, y, BLACK);
+ }
+ }
+ UpdateTextureRec(currentWorld->texture,
+ (Rectangle){ (float)(pattern.width*i), (float)(pattern.height*j),
+ (float)(pattern.width), (float)(pattern.height) }, pattern.data);
+ }
+ }
+ }
+
+ UnloadImage(pattern);
+
+ mode = MODE_PAUSE;
+ offsetX = worldWidth*presetPatterns[preset].position.x - windowWidth/zoom/2.0f;
+ offsetY = worldHeight*presetPatterns[preset].position.y - windowHeight/zoom/2.0f;
+ }
+
+ // Check window draw inside world limits
+ if (offsetX < 0) offsetX = 0;
+ if (offsetY < 0) offsetY = 0;
+ if (offsetX > worldWidth - (float)(windowWidth)/zoom) offsetX = worldWidth - (float)(windowWidth)/zoom;
+ if (offsetY > worldHeight - (float)(windowHeight)/zoom) offsetY = worldHeight - (float)(windowHeight)/zoom;
+
+ // Rectangles for drawing texture portion to screen
+ const Rectangle textureSourceToScreen = { offsetX, offsetY, (float)windowWidth/zoom, (float)windowHeight/zoom };
+ //----------------------------------------------------------------------------------
+
+ // Draw to texture
+ //----------------------------------------------------------------------------------
+ if ((mode == MODE_RUN) && ((frame%framesPerStep) == 0))
+ {
+ // Swap worlds
+ RenderTexture2D *tempWorld = currentWorld;
+ currentWorld = previousWorld;
+ previousWorld = tempWorld;
+
+ // Draw to texture
+ BeginTextureMode(*currentWorld);
+ BeginShaderMode(shdrGameOfLife);
+ DrawTexturePro(previousWorld->texture, worldRectSource, worldRectDest, (Vector2){ 0, 0 }, 0.0f, RAYWHITE);
+ EndShaderMode();
+ EndTextureMode();
+ }
+ //----------------------------------------------------------------------------------
+
+ // Draw to screen
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ DrawTexturePro(currentWorld->texture, textureSourceToScreen, textureOnScreen, (Vector2){ 0, 0 }, 0.0f, WHITE);
+
+ DrawLine(windowWidth, 0, windowWidth, screenHeight, (Color){ 218, 218, 218, 255 });
+ DrawRectangle(windowWidth, 0, screenWidth - windowWidth, screenHeight, (Color){ 232, 232, 232, 255 });
+
+ DrawText("Conway's", 704, 4, 20, DARKBLUE);
+ DrawText(" game of", 704, 19, 20, DARKBLUE);
+ DrawText(" life", 708, 34, 20, DARKBLUE);
+ DrawText("in raylib", 757, 42, 6, BLACK);
+
+ DrawText("Presets", 710, 58, 8, GRAY);
+ preset = -1;
+ for (int i = 0; i < numberOfPresets; i++)
+ if (GuiButton((Rectangle){ 710.0f, 70.0f + 18*i, 80.0f, 16.0f }, presetPatterns[i].name)) preset = i;
+
+ GuiToggleGroup((Rectangle){ 710, 258, 80, 16 }, "Run\nPause\nDraw", &mode);
+
+ DrawText(TextFormat("Zoom: %ix", zoom), 710, 316, 8, GRAY);
+ buttonZoomIn = GuiButton((Rectangle){ 710, 328, 80, 16 }, "Zoom in");
+ buttonZomOut = GuiButton((Rectangle){ 710, 346, 80, 16 }, "Zoom out");
+
+ DrawText(TextFormat("Speed: %i frame%s", framesPerStep, (framesPerStep > 1)? "s" : ""), 710, 370, 8, GRAY);
+ buttonFaster = GuiButton((Rectangle){ 710, 382, 80, 16 }, "Faster");
+ buttonSlower = GuiButton((Rectangle){ 710, 400, 80, 16 }, "Slower");
+
+ DrawFPS(712, 426);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ UnloadShader(shdrGameOfLife);
+ UnloadRenderTexture(world1);
+ UnloadRenderTexture(world2);
+
+ FreeImageToDraw(&imageToDraw);
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Functions definition
+//----------------------------------------------------------------------------------
+void FreeImageToDraw(Image **imageToDraw)
+{
+ if (*imageToDraw != NULL)
+ {
+ UnloadImage(**imageToDraw);
+ RL_FREE(*imageToDraw);
+ *imageToDraw = NULL;
+ }
+}
diff --git a/examples/shaders/shaders_game_of_life.png b/examples/shaders/shaders_game_of_life.png
new file mode 100644
index 000000000..8ebc2c12e
Binary files /dev/null and b/examples/shaders/shaders_game_of_life.png differ
diff --git a/examples/shaders/shaders_hybrid_rendering.c b/examples/shaders/shaders_hybrid_rendering.c
index ec6b8b0a6..439965fd6 100644
--- a/examples/shaders/shaders_hybrid_rendering.c
+++ b/examples/shaders/shaders_hybrid_rendering.c
@@ -118,7 +118,7 @@ int main(void)
// Raymarch Scene
rlEnableDepthTest(); // Manually enable Depth Test to handle multiple rendering methods
BeginShaderMode(shdrRaymarch);
- DrawRectangleRec((Rectangle){0,0, (float)screenWidth, (float)screenHeight},WHITE);
+ DrawRectangleRec((Rectangle){ 0,0, (float)screenWidth, (float)screenHeight },WHITE);
EndShaderMode();
// Rasterize Scene
@@ -138,6 +138,7 @@ int main(void)
ClearBackground(RAYWHITE);
DrawTextureRec(target.texture, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, (Vector2) { 0, 0 }, WHITE);
+
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
diff --git a/examples/shaders/shaders_lightmap_rendering.c b/examples/shaders/shaders_lightmap_rendering.c
index e269aada2..05d9c0e09 100644
--- a/examples/shaders/shaders_lightmap_rendering.c
+++ b/examples/shaders/shaders_lightmap_rendering.c
@@ -33,7 +33,7 @@
#define GLSL_VERSION 100
#endif
-#define MAP_SIZE 10
+#define MAP_SIZE 16
//------------------------------------------------------------------------------------
// Program main entry point
@@ -88,8 +88,6 @@ int main(void)
RenderTexture lightmap = LoadRenderTexture(MAP_SIZE, MAP_SIZE);
- SetTextureFilter(lightmap.texture, TEXTURE_FILTER_TRILINEAR);
-
Material material = LoadMaterialDefault();
material.shader = shader;
material.maps[MATERIAL_MAP_ALBEDO].texture = texture;
@@ -103,30 +101,34 @@ int main(void)
DrawTexturePro(
light,
(Rectangle){ 0, 0, (float)light.width, (float)light.height },
- (Rectangle){ 0, 0, 20, 20 },
- (Vector2){ 10.0, 10.0 },
+ (Rectangle){ 0, 0, 2.0f*MAP_SIZE, 2.0f*MAP_SIZE },
+ (Vector2){ (float)MAP_SIZE, (float)MAP_SIZE },
0.0,
RED
);
DrawTexturePro(
light,
(Rectangle){ 0, 0, (float)light.width, (float)light.height },
- (Rectangle){ 8, 4, 20, 20 },
- (Vector2){ 10.0, 10.0 },
+ (Rectangle){ (float)MAP_SIZE*0.8f, (float)MAP_SIZE/2.0f, 2.0f*MAP_SIZE, 2.0f*MAP_SIZE },
+ (Vector2){ (float)MAP_SIZE, (float)MAP_SIZE },
0.0,
BLUE
);
DrawTexturePro(
light,
(Rectangle){ 0, 0, (float)light.width, (float)light.height },
- (Rectangle){ 8, 8, 10, 10 },
- (Vector2){ 5.0, 5.0 },
+ (Rectangle){ (float)MAP_SIZE*0.8f, (float)MAP_SIZE*0.8f, (float)MAP_SIZE, (float)MAP_SIZE },
+ (Vector2){ (float)MAP_SIZE/2.0f, (float)MAP_SIZE/2.0f },
0.0,
GREEN
);
BeginBlendMode(BLEND_ALPHA);
EndTextureMode();
+ // NOTE: To enable trilinear filtering we need mipmaps available for texture
+ GenTextureMipmaps(&lightmap.texture);
+ SetTextureFilter(lightmap.texture, TEXTURE_FILTER_TRILINEAR);
+
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
@@ -141,24 +143,20 @@ int main(void)
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
+
ClearBackground(RAYWHITE);
BeginMode3D(camera);
DrawMesh(mesh, material, MatrixIdentity());
EndMode3D();
- DrawFPS(10, 10);
-
- DrawTexturePro(
- lightmap.texture,
- (Rectangle){ 0, 0, -MAP_SIZE, -MAP_SIZE },
+ DrawTexturePro(lightmap.texture, (Rectangle){ 0, 0, -MAP_SIZE, -MAP_SIZE },
(Rectangle){ (float)GetRenderWidth() - MAP_SIZE*8 - 10, 10, (float)MAP_SIZE*8, (float)MAP_SIZE*8 },
- (Vector2){ 0.0, 0.0 },
- 0.0,
- WHITE);
+ (Vector2){ 0.0, 0.0 }, 0.0, WHITE);
- DrawText("lightmap", GetRenderWidth() - 66, 16 + MAP_SIZE*8, 10, GRAY);
- DrawText("10x10 pixels", GetRenderWidth() - 76, 30 + MAP_SIZE*8, 10, GRAY);
+ DrawText(TextFormat("LIGHTMAP: %ix%i pixels", MAP_SIZE, MAP_SIZE), GetRenderWidth() - 130, 20 + MAP_SIZE*8, 10, GREEN);
+
+ DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
diff --git a/examples/shaders/shaders_shadowmap_rendering.c b/examples/shaders/shaders_shadowmap_rendering.c
index 7b75b80e0..a739cdd93 100644
--- a/examples/shaders/shaders_shadowmap_rendering.c
+++ b/examples/shaders/shaders_shadowmap_rendering.c
@@ -45,7 +45,7 @@ int main(void)
// Shadows are a HUGE topic, and this example shows an extremely simple implementation of the shadowmapping algorithm,
// which is the industry standard for shadows. This algorithm can be extended in a ridiculous number of ways to improve
// realism and also adapt it for different scenes. This is pretty much the simplest possible implementation
-
+
SetConfigFlags(FLAG_MSAA_4X_HINT);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shadowmap rendering");
@@ -59,7 +59,7 @@ int main(void)
Shader shadowShader = LoadShader(TextFormat("resources/shaders/glsl%i/shadowmap.vs", GLSL_VERSION),
TextFormat("resources/shaders/glsl%i/shadowmap.fs", GLSL_VERSION));
shadowShader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shadowShader, "viewPos");
-
+
Vector3 lightDir = Vector3Normalize((Vector3){ 0.35f, -1.0f, -0.35f });
Color lightColor = WHITE;
Vector4 lightColorNormalized = ColorNormalize(lightColor);
@@ -83,7 +83,7 @@ int main(void)
ModelAnimation *robotAnimations = LoadModelAnimations("resources/models/robot.glb", &animCount);
RenderTexture2D shadowMap = LoadShadowmapRenderTexture(SHADOWMAP_RESOLUTION, SHADOWMAP_RESOLUTION);
-
+
// For the shadowmapping algorithm, we will be rendering everything from the light's point of view
Camera3D lightCamera = { 0 };
lightCamera.position = Vector3Scale(lightDir, -15.0f);
@@ -91,9 +91,9 @@ int main(void)
lightCamera.projection = CAMERA_ORTHOGRAPHIC; // Use an orthographic projection for directional lights
lightCamera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
lightCamera.fovy = 20.0f;
-
+
int frameCounter = 0;
-
+
// Store the light matrices
Matrix lightView = { 0 };
Matrix lightProj = { 0 };
@@ -136,7 +136,7 @@ int main(void)
{
if (lightDir.z > -0.6f) lightDir.z -= cameraSpeed*60.0f*deltaTime;
}
-
+
lightDir = Vector3Normalize(lightDir);
lightCamera.position = Vector3Scale(lightDir, -15.0f);
SetShaderValue(shadowShader, lightDirLoc, &lightDir, SHADER_UNIFORM_VEC3);
@@ -151,13 +151,13 @@ int main(void)
// to determine whether a given point is "visible" to the light
BeginTextureMode(shadowMap);
ClearBackground(WHITE);
-
+
BeginMode3D(lightCamera);
lightView = rlGetMatrixModelview();
lightProj = rlGetMatrixProjection();
DrawScene(cube, robot);
EndMode3D();
-
+
EndTextureMode();
lightViewProj = MatrixMultiply(lightView, lightProj);
@@ -167,7 +167,7 @@ int main(void)
SetShaderValueMatrix(shadowShader, lightVPLoc, lightViewProj);
rlEnableShader(shadowShader.id);
-
+
rlActiveTextureSlot(textureActiveSlot);
rlEnableTexture(shadowMap.depth.id);
rlSetUniform(shadowMapLoc, &textureActiveSlot, SHADER_UNIFORM_INT, 1);
@@ -178,7 +178,7 @@ int main(void)
DrawText("Use the arrow keys to rotate the light!", 10, 10, 30, RED);
DrawText("Shadows in raylib using the shadowmapping algorithm!", screenWidth - 280, screenHeight - 20, 10, GRAY);
-
+
EndDrawing();
if (IsKeyPressed(KEY_F)) TakeScreenshot("shaders_shadowmap.png");
@@ -200,7 +200,7 @@ int main(void)
}
// Load render texture for shadowmap projection
-// NOTE: Load framebuffer with only a texture depth attachment,
+// NOTE: Load framebuffer with only a texture depth attachment,
// no color attachment required for shadowmap
static RenderTexture2D LoadShadowmapRenderTexture(int width, int height)
{
diff --git a/examples/shaders/shaders_spotlight_rendering.c b/examples/shaders/shaders_spotlight_rendering.c
index c9ee950c1..3678f9796 100644
--- a/examples/shaders/shaders_spotlight_rendering.c
+++ b/examples/shaders/shaders_spotlight_rendering.c
@@ -239,7 +239,7 @@ int main(void)
static void ResetStar(Star *star)
{
star->position = (Vector2){ GetScreenWidth()/2.0f, GetScreenHeight()/2.0f };
-
+
star->speed.x = (float)GetRandomValue(-1000, 1000)/100.0f;
star->speed.y = (float)GetRandomValue(-1000, 1000)/100.0f;
@@ -247,7 +247,7 @@ static void ResetStar(Star *star)
{
star->speed.x = (float)GetRandomValue(-1000, 1000)/100.0f;
star->speed.y = (float)GetRandomValue(-1000, 1000)/100.0f;
- }
+ }
star->position = Vector2Add(star->position, Vector2Multiply(star->speed, (Vector2){ 8.0f, 8.0f }));
}
diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h
index a3fc51f0f..88fe5cc5b 100644
--- a/examples/shapes/raygui.h
+++ b/examples/shapes/raygui.h
@@ -4,7 +4,7 @@
*
* DESCRIPTION:
* raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also
-* available as a standalone library, as long as input and drawing functions are provided.
+* available as a standalone library, as long as input and drawing functions are provided
*
* FEATURES:
* - Immediate-mode gui, minimal retained data
@@ -27,7 +27,7 @@
* - WARNING: GuiLoadStyle() and GuiLoadStyle{Custom}() functions, allocate memory for
* font atlas recs and glyphs, freeing that memory is (usually) up to the user,
* no unload function is explicitly provided... but note that GuiLoadStyleDefault() unloads
-* by default any previously loaded font (texture, recs, glyphs).
+* by default any previously loaded font (texture, recs, glyphs)
* - Global UI alpha (guiAlpha) is applied inside GuiDrawRectangle() and GuiDrawText() functions
*
* CONTROLS PROVIDED:
@@ -65,7 +65,7 @@
* - MessageBox --> Window, Label, Button
* - TextInputBox --> Window, Label, TextBox, Button
*
-* It also provides a set of functions for styling the controls based on its properties (size, color).
+* It also provides a set of functions for styling the controls based on its properties (size, color)
*
*
* RAYGUI STYLE (guiStyle):
@@ -77,11 +77,11 @@
*
* static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)];
*
-* guiStyle size is by default: 16*(16 + 8) = 384*4 = 1536 bytes = 1.5 KB
+* guiStyle size is by default: 16*(16 + 8) = 384 int = 384*4 bytes = 1536 bytes = 1.5 KB
*
* Note that the first set of BASE properties (by default guiStyle[0..15]) belong to the generic style
* used for all controls, when any of those base values is set, it is automatically populated to all
-* controls, so, specific control values overwriting generic style should be set after base values.
+* controls, so, specific control values overwriting generic style should be set after base values
*
* After the first BASE set we have the EXTENDED properties (by default guiStyle[16..23]), those
* properties are actually common to all controls and can not be overwritten individually (like BASE ones)
@@ -100,7 +100,7 @@
* Every icon is codified in binary form, using 1 bit per pixel, so, every 16x16 icon
* requires 8 integers (16*16/32) to be stored in memory.
*
-* When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set.
+* When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set
*
* The global icons array size is fixed and depends on the number of icons and size:
*
@@ -112,20 +112,20 @@
*
* RAYGUI LAYOUT:
* raygui currently does not provide an auto-layout mechanism like other libraries,
-* layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it.
+* layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it
*
* TOOL: rGuiLayout is a visual tool to create raygui layouts: github.com/raysan5/rguilayout
*
* CONFIGURATION:
* #define RAYGUI_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 RAYGUI_STANDALONE
* Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined
* internally in the library and input management and drawing functions must be provided by
-* the user (check library implementation for further details).
+* the user (check library implementation for further details)
*
* #define RAYGUI_NO_ICONS
* Avoid including embedded ricons data (256 icons, 16x16 pixels, 1-bit per pixel, 2KB)
@@ -141,12 +141,17 @@
* Draw text bounds rectangles for debug
*
* VERSIONS HISTORY:
-* 4.5-dev (Sep-2024) Current dev version...
+* 5.0 (xx-Nov-2025) ADDED: Support up to 32 controls (v500)
* ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes
* ADDED: GuiValueBoxFloat()
* ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP
* ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH
+* ADDED: GuiLoadIconsFromMemory()
* ADDED: Multiple new icons
+* REMOVED: GuiSpinner() from controls list, using BUTTON + VALUEBOX properties
+* REMOVED: GuiSliderPro(), functionality was redundant
+* REVIEWED: Controls using text labels to use LABEL properties
+* REVIEWED: Replaced sprintf() by snprintf() for more safety
* REVIEWED: GuiTabBar(), close tab with mouse middle button
* REVIEWED: GuiScrollPanel(), scroll speed proportional to content
* REVIEWED: GuiDropdownBox(), support roll up and hidden arrow
@@ -156,6 +161,8 @@
* REVIEWED: GuiIconText(), increase buffer size and reviewed padding
* REVIEWED: GuiDrawText(), improved wrap mode drawing
* REVIEWED: GuiScrollBar(), minor tweaks
+* REVIEWED: GuiProgressBar(), improved borders computing
+* REVIEWED: GuiTextBox(), multiple improvements: autocursor and more
* REVIEWED: Functions descriptions, removed wrong return value reference
* REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion
*
@@ -259,16 +266,16 @@
* 1.4 (15-Jun-2017) Rewritten all GUI functions (removed useless ones)
* 1.3 (12-Jun-2017) Complete redesign of style system
* 1.1 (01-Jun-2017) Complete review of the library
-* 1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria.
-* 0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria.
-* 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria.
+* 1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria
+* 0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria
+* 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria
*
* DEPENDENCIES:
-* raylib 5.0 - Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing
+* raylib 5.6-dev - Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing
*
* STANDALONE MODE:
* By default raygui depends on raylib mostly for the inputs and the drawing functionality but that dependency can be disabled
-* with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs.
+* with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs
*
* The following functions should be redefined for a custom backend:
*
@@ -309,7 +316,7 @@
*
* LICENSE: zlib/libpng
*
-* Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+* 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.
@@ -334,7 +341,7 @@
#define RAYGUI_VERSION_MAJOR 4
#define RAYGUI_VERSION_MINOR 5
#define RAYGUI_VERSION_PATCH 0
-#define RAYGUI_VERSION "4.5-dev"
+#define RAYGUI_VERSION "5.0-dev"
#if !defined(RAYGUI_STANDALONE)
#include "raylib.h"
@@ -358,17 +365,6 @@
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
-// Allow custom memory allocators
-#ifndef RAYGUI_MALLOC
- #define RAYGUI_MALLOC(sz) malloc(sz)
-#endif
-#ifndef RAYGUI_CALLOC
- #define RAYGUI_CALLOC(n,sz) calloc(n,sz)
-#endif
-#ifndef RAYGUI_FREE
- #define RAYGUI_FREE(p) free(p)
-#endif
-
// Simple log system to avoid printf() calls if required
// NOTE: Avoiding those calls, also avoids const strings memory usage
#define RAYGUI_SUPPORT_LOG_INFO
@@ -421,13 +417,16 @@
// TODO: Texture2D type is very coupled to raylib, required by Font type
// It should be redesigned to be provided by user
- typedef struct Texture2D {
+ typedef struct Texture {
unsigned int id; // OpenGL texture id
int width; // Texture base width
int height; // Texture base height
int mipmaps; // Mipmap levels, 1 by default
int format; // Data format (PixelFormat type)
- } Texture2D;
+ } Texture;
+
+ // Texture2D, same as Texture
+ typedef Texture Texture2D;
// Image, pixel data stored in CPU memory (RAM)
typedef struct Image {
@@ -527,7 +526,7 @@ typedef enum {
DROPDOWNBOX,
TEXTBOX, // Used also for: TEXTBOXMULTI
VALUEBOX,
- SPINNER, // Uses: BUTTON, VALUEBOX
+ CONTROL11,
LISTVIEW,
COLORPICKER,
SCROLLBAR,
@@ -549,12 +548,12 @@ typedef enum {
BORDER_COLOR_DISABLED, // Control border color in STATE_DISABLED
BASE_COLOR_DISABLED, // Control base color in STATE_DISABLED
TEXT_COLOR_DISABLED, // Control text color in STATE_DISABLED
- BORDER_WIDTH, // Control border size, 0 for no border
+ BORDER_WIDTH = 12, // Control border size, 0 for no border
//TEXT_SIZE, // Control text size (glyphs max height) -> GLOBAL for all controls
//TEXT_SPACING, // Control text spacing between glyphs -> GLOBAL for all controls
- //TEXT_LINE_SPACING // Control text spacing between lines -> GLOBAL for all controls
- TEXT_PADDING, // Control text padding, not considering border
- TEXT_ALIGNMENT, // Control text horizontal alignment inside control text bound (after border and padding)
+ //TEXT_LINE_SPACING, // Control text spacing between lines -> GLOBAL for all controls
+ TEXT_PADDING = 13, // Control text padding, not considering border
+ TEXT_ALIGNMENT = 14, // Control text horizontal alignment inside control text bound (after border and padding)
//TEXT_WRAP_MODE // Control text wrap-mode inside text bounds -> GLOBAL for all controls
} GuiControlProperty;
@@ -641,11 +640,14 @@ typedef enum {
TEXT_READONLY = 16, // TextBox in read-only mode: 0-text editable, 1-text no-editable
} GuiTextBoxProperty;
-// Spinner
+// ValueBox/Spinner
typedef enum {
- SPIN_BUTTON_WIDTH = 16, // Spinner left/right buttons width
- SPIN_BUTTON_SPACING, // Spinner buttons separation
-} GuiSpinnerProperty;
+ SPINNER_BUTTON_WIDTH = 16, // Spinner left/right buttons width
+ SPINNER_BUTTON_SPACING, // Spinner buttons separation
+} GuiValueBoxProperty;
+
+// Control11
+//typedef enum { } GuiControl11Property;
// ListView
typedef enum {
@@ -653,6 +655,7 @@ typedef enum {
LIST_ITEMS_SPACING, // ListView items separation
SCROLLBAR_WIDTH, // ListView scrollbar size (usually width)
SCROLLBAR_SIDE, // ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE)
+ LIST_ITEMS_BORDER_NORMAL, // ListView items border enabled in normal state
LIST_ITEMS_BORDER_WIDTH // ListView items border width
} GuiListViewProperty;
@@ -717,6 +720,9 @@ RAYGUIAPI char **GuiLoadIcons(const char *fileName, bool loadIconsName); // Load
RAYGUIAPI void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color); // Draw icon using pixel size at specified position
#endif
+// Utility functions
+RAYGUIAPI int GuiGetTextWidth(const char *text); // Get text width considering gui style and icon size (if required)
+
// Controls
//----------------------------------------------------------------------------------------------------------
// Container/separator controls, useful for controls organization
@@ -999,33 +1005,33 @@ typedef enum {
ICON_MLAYERS = 226,
ICON_MAPS = 227,
ICON_HOT = 228,
- ICON_229 = 229,
- ICON_230 = 230,
- ICON_231 = 231,
- ICON_232 = 232,
- ICON_233 = 233,
- ICON_234 = 234,
- ICON_235 = 235,
- ICON_236 = 236,
- ICON_237 = 237,
- ICON_238 = 238,
- ICON_239 = 239,
- ICON_240 = 240,
- ICON_241 = 241,
- ICON_242 = 242,
- ICON_243 = 243,
- ICON_244 = 244,
- ICON_245 = 245,
- ICON_246 = 246,
- ICON_247 = 247,
- ICON_248 = 248,
- ICON_249 = 249,
+ ICON_LABEL = 229,
+ ICON_NAME_ID = 230,
+ ICON_SLICING = 231,
+ ICON_MANUAL_CONTROL = 232,
+ ICON_COLLISION = 233,
+ ICON_CIRCLE_ADD = 234,
+ ICON_CIRCLE_ADD_FILL = 235,
+ ICON_CIRCLE_WARNING = 236,
+ ICON_CIRCLE_WARNING_FILL = 237,
+ ICON_BOX_MORE = 238,
+ ICON_BOX_MORE_FILL = 239,
+ ICON_BOX_MINUS = 240,
+ ICON_BOX_MINUS_FILL = 241,
+ ICON_UNION = 242,
+ ICON_INTERSECTION = 243,
+ ICON_DIFFERENCE = 244,
+ ICON_SPHERE = 245,
+ ICON_CYLINDER = 246,
+ ICON_CONE = 247,
+ ICON_ELLIPSOID = 248,
+ ICON_CAPSULE = 249,
ICON_250 = 250,
ICON_251 = 251,
ICON_252 = 252,
ICON_253 = 253,
ICON_254 = 254,
- ICON_255 = 255,
+ ICON_255 = 255
} GuiIconName;
#endif
@@ -1046,12 +1052,24 @@ typedef enum {
#if defined(RAYGUI_IMPLEMENTATION)
#include // required for: isspace() [GuiTextBox()]
-#include // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()]
-#include // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]
+#include // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), snprintf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()]
#include // Required for: strlen() [GuiTextBox(), GuiValueBox()], memset(), memcpy()
#include // Required for: va_list, va_start(), vfprintf(), va_end() [TextFormat()]
#include // Required for: roundf() [GuiColorPicker()]
+// Allow custom memory allocators
+#if defined(RAYGUI_MALLOC) || defined(RAYGUI_CALLOC) || defined(RAYGUI_FREE)
+ #if !defined(RAYGUI_MALLOC) || !defined(RAYGUI_CALLOC) || !defined(RAYGUI_FREE)
+ #error "RAYGUI: if RAYGUI_MALLOC, RAYGUI_CALLOC, or RAYGUI_FREE is customized, all three must be customized"
+ #endif
+#else
+ #include // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]
+
+ #define RAYGUI_MALLOC(sz) malloc(sz)
+ #define RAYGUI_CALLOC(n,sz) calloc(n,sz)
+ #define RAYGUI_FREE(p) free(p)
+#endif
+
#ifdef __cplusplus
#define RAYGUI_CLITERAL(name) name
#else
@@ -1060,7 +1078,7 @@ typedef enum {
// Check if two rectangles are equal, used to validate a slider bounds as an id
#ifndef CHECK_BOUNDS_ID
- #define CHECK_BOUNDS_ID(src, dst) ((src.x == dst.x) && (src.y == dst.y) && (src.width == dst.width) && (src.height == dst.height))
+ #define CHECK_BOUNDS_ID(src, dst) (((int)src.x == (int)dst.x) && ((int)src.y == (int)dst.y) && ((int)src.width == (int)dst.width) && ((int)src.height == (int)dst.height))
#endif
#if !defined(RAYGUI_NO_ICONS) && !defined(RAYGUI_CUSTOM_ICONS)
@@ -1318,27 +1336,27 @@ static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] =
0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x422a422a, 0x422e422a, 0x40384e28, 0x00007fe0, // ICON_MLAYERS
0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x5b2a512a, 0x512e552a, 0x40385128, 0x00007fe0, // ICON_MAPS
0x04200000, 0x1cf00c60, 0x11f019f0, 0x0f3807b8, 0x1e3c0f3c, 0x1c1c1e1c, 0x1e3c1c1c, 0x00000f70, // ICON_HOT
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_229
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_230
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_231
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_232
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_233
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_234
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_235
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_236
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_237
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_238
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_239
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_240
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_241
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_242
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_243
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_244
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_245
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_246
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_247
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_248
- 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_249
+ 0x00000000, 0x20803f00, 0x2a202e40, 0x20082e10, 0x08021004, 0x02040402, 0x00900108, 0x00000060, // ICON_LABEL
+ 0x00000000, 0x042007e0, 0x47e27c3e, 0x4ffa4002, 0x47fa4002, 0x4ffa4002, 0x7ffe4002, 0x00000000, // ICON_NAME_ID
+ 0x7fe00000, 0x402e4020, 0x43ce5e0a, 0x40504078, 0x438e4078, 0x402e5e0a, 0x7fe04020, 0x00000000, // ICON_SLICING
+ 0x00000000, 0x40027ffe, 0x47c24002, 0x55425d42, 0x55725542, 0x50125552, 0x10105016, 0x00001ff0, // ICON_MANUAL_CONTROL
+ 0x7ffe0000, 0x43c24002, 0x48124422, 0x500a500a, 0x500a500a, 0x44224812, 0x400243c2, 0x00007ffe, // ICON_COLLISION
+ 0x03c00000, 0x10080c30, 0x21842184, 0x4ff24182, 0x41824ff2, 0x21842184, 0x0c301008, 0x000003c0, // ICON_CIRCLE_ADD
+ 0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x700e7e7e, 0x7e7e700e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0, // ICON_CIRCLE_ADD_FILL
+ 0x03c00000, 0x10080c30, 0x21842184, 0x41824182, 0x40024182, 0x21842184, 0x0c301008, 0x000003c0, // ICON_CIRCLE_WARNING
+ 0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x7e7e7e7e, 0x7ffe7e7e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0, // ICON_CIRCLE_WARNING_FILL
+ 0x00000000, 0x10041ffc, 0x10841004, 0x13e41084, 0x10841084, 0x10041004, 0x00001ffc, 0x00000000, // ICON_BOX_MORE
+ 0x00000000, 0x1ffc1ffc, 0x1f7c1ffc, 0x1c1c1f7c, 0x1f7c1f7c, 0x1ffc1ffc, 0x00001ffc, 0x00000000, // ICON_BOX_MORE_FILL
+ 0x00000000, 0x1ffc1ffc, 0x1ffc1ffc, 0x1c1c1ffc, 0x1ffc1ffc, 0x1ffc1ffc, 0x00001ffc, 0x00000000, // ICON_BOX_MINUS
+ 0x00000000, 0x10041ffc, 0x10041004, 0x13e41004, 0x10041004, 0x10041004, 0x00001ffc, 0x00000000, // ICON_BOX_MINUS_FILL
+ 0x07fe0000, 0x055606aa, 0x7ff606aa, 0x55766eba, 0x55766eaa, 0x55606ffe, 0x55606aa0, 0x00007fe0, // ICON_UNION
+ 0x07fe0000, 0x04020402, 0x7fe20402, 0x456246a2, 0x456246a2, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_INTERSECTION
+ 0x07fe0000, 0x055606aa, 0x7ff606aa, 0x4436442a, 0x4436442a, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_DIFFERENCE
+ 0x03c00000, 0x10080c30, 0x20042004, 0x60064002, 0x47e2581a, 0x20042004, 0x0c301008, 0x000003c0, // ICON_SPHERE
+ 0x03e00000, 0x08080410, 0x0c180808, 0x08080be8, 0x08080808, 0x08080808, 0x04100808, 0x000003e0, // ICON_CYLINDER
+ 0x00800000, 0x01400140, 0x02200220, 0x04100410, 0x08080808, 0x1c1c13e4, 0x08081004, 0x000007f0, // ICON_CONE
+ 0x00000000, 0x07e00000, 0x20841918, 0x40824082, 0x40824082, 0x19182084, 0x000007e0, 0x00000000, // ICON_ELLIPSOID
+ 0x00000000, 0x00000000, 0x20041ff8, 0x40024002, 0x40024002, 0x1ff82004, 0x00000000, 0x00000000, // ICON_CAPSULE
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_250
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_251
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_252
@@ -1363,7 +1381,7 @@ static unsigned int *guiIconsPtr = guiIcons;
#define RAYGUI_MAX_PROPS_EXTENDED 8 // Maximum number of extended properties
//----------------------------------------------------------------------------------
-// Types and Structures Definition
+// Module Types and Structures Definition
//----------------------------------------------------------------------------------
// Gui control property style color element
typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement;
@@ -1387,8 +1405,7 @@ static Rectangle guiControlExclusiveRec = { 0 }; // Gui control exclusive bounds
static int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*()
//static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking
-static int autoCursorCooldownCounter = 0; // Cooldown frame counter for automatic cursor movement on key-down
-static int autoCursorDelayCounter = 0; // Delay frame counter for automatic cursor movement
+static int autoCursorCounter = 0; // Frame counter for automatic repeated cursor movement on key-down (cooldown and delay)
//----------------------------------------------------------------------------------
// Style data array for all gui style properties (allocated on data segment by default)
@@ -1484,7 +1501,6 @@ static void DrawRectangleGradientV(int posX, int posY, int width, int height, Co
//----------------------------------------------------------------------------------
static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize); // Load style from memory (binary only)
-static int GetTextWidth(const char *text); // Gui get text width using gui font and style
static Rectangle GetTextBounds(int control, Rectangle bounds); // Get text bounds considering control bounds
static const char *GetTextIcon(const char *text, int *iconId); // Get text icon if provided and move text cursor
@@ -1589,6 +1605,10 @@ int GuiWindowBox(Rectangle bounds, const char *title)
#define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT 24
#endif
+ #if !defined(RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT)
+ #define RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT 18
+ #endif
+
int result = 0;
//GuiState state = guiState;
@@ -1597,9 +1617,10 @@ int GuiWindowBox(Rectangle bounds, const char *title)
Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)statusBarHeight };
if (bounds.height < statusBarHeight*2.0f) bounds.height = statusBarHeight*2.0f;
+ const float vPadding = statusBarHeight/2.0f - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT/2.0f;
Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - 1, bounds.width, bounds.height - (float)statusBarHeight + 1 };
- Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - 20,
- statusBar.y + statusBarHeight/2.0f - 18.0f/2.0f, 18, 18 };
+ Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding,
+ statusBar.y + vPadding, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT };
// Update control
//--------------------------------------------------------------------
@@ -1653,7 +1674,7 @@ int GuiGroupBox(Rectangle bounds, const char *text)
// Line control
int GuiLine(Rectangle bounds, const char *text)
{
- #if !defined(RAYGUI_LINE_ORIGIN_SIZE)
+ #if !defined(RAYGUI_LINE_MARGIN_TEXT)
#define RAYGUI_LINE_MARGIN_TEXT 12
#endif
#if !defined(RAYGUI_LINE_TEXT_PADDING)
@@ -1671,7 +1692,7 @@ int GuiLine(Rectangle bounds, const char *text)
else
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(text) + 2;
+ textBounds.width = (float)GuiGetTextWidth(text) + 2;
textBounds.height = bounds.height;
textBounds.x = bounds.x + RAYGUI_LINE_MARGIN_TEXT;
textBounds.y = bounds.y;
@@ -1711,8 +1732,8 @@ int GuiPanel(Rectangle bounds, const char *text)
//--------------------------------------------------------------------
if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar
- GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED: (int)LINE_COLOR)),
- GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR)));
+ GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)),
+ GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BASE_COLOR_DISABLED : (int)BACKGROUND_COLOR)));
//--------------------------------------------------------------------
return result;
@@ -1722,7 +1743,7 @@ int GuiPanel(Rectangle bounds, const char *text)
// NOTE: Using GuiToggle() for the TABS
int GuiTabBar(Rectangle bounds, const char **text, int count, int *active)
{
- #define RAYGUI_TABBAR_ITEM_WIDTH 160
+ #define RAYGUI_TABBAR_ITEM_WIDTH 148
int result = -1;
//GuiState state = guiState;
@@ -1755,12 +1776,12 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active)
if (i == (*active))
{
toggle = true;
- GuiToggle(tabBounds, GuiIconText(12, text[i]), &toggle);
+ GuiToggle(tabBounds, text[i], &toggle);
}
else
{
toggle = false;
- GuiToggle(tabBounds, GuiIconText(12, text[i]), &toggle);
+ GuiToggle(tabBounds, text[i], &toggle);
if (toggle) *active = i;
}
@@ -2011,7 +2032,7 @@ int GuiLabelButton(Rectangle bounds, const char *text)
bool pressed = false;
// NOTE: We force bounds.width to be all text
- float textWidth = (float)GetTextWidth(text);
+ float textWidth = (float)GuiGetTextWidth(text);
if ((bounds.width - 2*GuiGetStyle(LABEL, BORDER_WIDTH) - 2*GuiGetStyle(LABEL, TEXT_PADDING)) < textWidth) bounds.width = textWidth + 2*GuiGetStyle(LABEL, BORDER_WIDTH) + 2*GuiGetStyle(LABEL, TEXT_PADDING) + 2;
// Update control
@@ -2149,7 +2170,9 @@ int GuiToggleSlider(Rectangle bounds, const char *text, int *active)
// Get substrings items from text (items pointers)
int itemCount = 0;
- const char **items = GuiTextSplit(text, ';', &itemCount, NULL);
+ const char **items = NULL;
+
+ if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL);
Rectangle slider = {
0, // Calculated later depending on the active toggle
@@ -2196,7 +2219,7 @@ int GuiToggleSlider(Rectangle bounds, const char *text, int *active)
if (text != NULL)
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(text);
+ textBounds.width = (float)GuiGetTextWidth(text);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = slider.x + slider.width/2 - textBounds.width/2;
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -2221,7 +2244,7 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked)
if (text != NULL)
{
- textBounds.width = (float)GetTextWidth(text) + 2;
+ textBounds.width = (float)GuiGetTextWidth(text) + 2;
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -2474,7 +2497,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod
int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
{
#if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)
- #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 40 // Frames to wait for autocursor movement
+ #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 20 // Frames to wait for autocursor movement
#endif
#if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY)
#define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY 1 // Frames delay for autocursor movement
@@ -2487,10 +2510,10 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE);
Rectangle textBounds = GetTextBounds(TEXTBOX, bounds);
- int textLength = (int)strlen(text); // Get current text length
+ int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length
int thisCursorIndex = textBoxCursorIndex;
if (thisCursorIndex > textLength) thisCursorIndex = textLength;
- int textWidth = GetTextWidth(text) - GetTextWidth(text + thisCursorIndex);
+ int textWidth = GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex);
int textIndexOffset = 0; // Text index offset to start drawing in the box
// Cursor rectangle
@@ -2511,15 +2534,6 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
mouseCursor.x = -1;
mouseCursor.width = 1;
- // Auto-cursor movement logic
- // NOTE: Cursor moves automatically when key down after some time
- if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCooldownCounter++;
- else
- {
- autoCursorCooldownCounter = 0; // GLOBAL: Cursor cooldown counter
- autoCursorDelayCounter = 0; // GLOBAL: Cursor delay counter
- }
-
// Blink-cursor frame counter
//if (!autoCursorMode) blinkCursorFrameCounter++;
//else blinkCursorFrameCounter = 0;
@@ -2537,6 +2551,13 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
if (editMode)
{
+ // GLOBAL: Auto-cursor movement logic
+ // NOTE: Keystrokes are handled repeatedly when button is held down for some time
+ if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCounter++;
+ else autoCursorCounter = 0;
+
+ bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0);
+
state = STATE_PRESSED;
if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
@@ -2550,7 +2571,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
textIndexOffset += nextCodepointSize;
- textWidth = GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex);
+ textWidth = GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex);
}
int codepoint = GetCharPressed(); // Get Unicode codepoint
@@ -2560,10 +2581,43 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
int codepointSize = 0;
const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize);
- // Add codepoint to text, at current cursor position
- // NOTE: Make sure we do not overflow buffer size
- if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize))
+ // Handle text paste action
+ if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
{
+ const char *pasteText = GetClipboardText();
+ if (pasteText != NULL)
+ {
+ int pasteLength = 0;
+ int pasteCodepoint;
+ int pasteCodepointSize;
+
+ // Count how many codepoints to copy, stopping at the first unwanted control character
+ while (true)
+ {
+ pasteCodepoint = GetCodepointNext(pasteText + pasteLength, &pasteCodepointSize);
+ if (textLength + pasteLength + pasteCodepointSize >= textSize) break;
+ if (!(multiline && (pasteCodepoint == (int)'\n')) && !(pasteCodepoint >= 32)) break;
+ pasteLength += pasteCodepointSize;
+ }
+
+ if (pasteLength > 0)
+ {
+ // Move forward data from cursor position
+ for (int i = textLength + pasteLength; i > textBoxCursorIndex; i--) text[i] = text[i - pasteLength];
+
+ // Paste data in at cursor
+ for (int i = 0; i < pasteLength; i++) text[textBoxCursorIndex + i] = pasteText[i];
+
+ textBoxCursorIndex += pasteLength;
+ textLength += pasteLength;
+ text[textLength] = '\0';
+ }
+ }
+ }
+ else if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize))
+ {
+ // Adding codepoint to text, at current cursor position
+
// Move forward data from cursor position
for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize];
@@ -2583,113 +2637,185 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
// Move cursor to end
if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) textBoxCursorIndex = textLength;
- // Delete codepoint from text, after current cursor position
- if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))))
+ // Delete related codepoints from text, after current cursor position
+ if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
{
- autoCursorDelayCounter++;
+ int offset = textBoxCursorIndex;
+ int accCodepointSize = 0;
+ int nextCodepointSize;
+ int nextCodepoint;
- if (IsKeyPressed(KEY_DELETE) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames
+ // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
+ // Not using isalnum() since it only works on ASCII characters
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+ bool puctuation = ispunct(nextCodepoint & 0xff);
+ while (offset < textLength)
{
- int nextCodepointSize = 0;
- GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
-
- // Move backward text from cursor position
- for (int i = textBoxCursorIndex; i < textLength; i++) text[i] = text[i + nextCodepointSize];
-
- textLength -= codepointSize;
- if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
-
- // Make sure text last character is EOL
- text[textLength] = '\0';
+ if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff))))
+ break;
+ offset += nextCodepointSize;
+ accCodepointSize += nextCodepointSize;
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
}
+
+ // Check whitespace to delete (ASCII only)
+ while (offset < textLength)
+ {
+ if (!isspace(nextCodepoint & 0xff)) break;
+
+ offset += nextCodepointSize;
+ accCodepointSize += nextCodepointSize;
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+ }
+
+ // Move text after cursor forward (including final null terminator)
+ for (int i = offset; i <= textLength; i++) text[i - accCodepointSize] = text[i];
+
+ textLength -= accCodepointSize;
+ }
+
+ else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger)))
+ {
+ // Delete single codepoint from text, after current cursor position
+
+ int nextCodepointSize = 0;
+ GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+
+ // Move text after cursor forward (including final null terminator)
+ for (int i = textBoxCursorIndex + nextCodepointSize; i <= textLength; i++) text[i - nextCodepointSize] = text[i];
+
+ textLength -= nextCodepointSize;
}
// Delete related codepoints from text, before current cursor position
- if ((textLength > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+ if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
{
- int i = textBoxCursorIndex - 1;
+ int offset = textBoxCursorIndex;
int accCodepointSize = 0;
+ int prevCodepointSize;
+ int prevCodepoint;
- // Move cursor to the end of word if on space already
- while ((i > 0) && isspace(text[i]))
+ // Check whitespace to delete (ASCII only)
+ while (offset > 0)
{
- int prevCodepointSize = 0;
- GetCodepointPrevious(text + i, &prevCodepointSize);
- i -= prevCodepointSize;
+ prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+ if (!isspace(prevCodepoint & 0xff)) break;
+
+ offset -= prevCodepointSize;
accCodepointSize += prevCodepointSize;
}
- // Move cursor to the start of the word
- while ((i > 0) && !isspace(text[i]))
+ // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
+ // Not using isalnum() since it only works on ASCII characters
+ bool puctuation = ispunct(prevCodepoint & 0xff);
+ while (offset > 0)
{
- int prevCodepointSize = 0;
- GetCodepointPrevious(text + i, &prevCodepointSize);
- i -= prevCodepointSize;
+ prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+ if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
+
+ offset -= prevCodepointSize;
accCodepointSize += prevCodepointSize;
}
- // Move forward text from cursor position
- for (int j = (textBoxCursorIndex - accCodepointSize); j < textLength; j++) text[j] = text[j + accCodepointSize];
+ // Move text after cursor forward (including final null terminator)
+ for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - accCodepointSize] = text[i];
- // Prevent cursor index from decrementing past 0
- if (textBoxCursorIndex > 0)
- {
- textBoxCursorIndex -= accCodepointSize;
- textLength -= accCodepointSize;
- }
+ textLength -= accCodepointSize;
+ textBoxCursorIndex -= accCodepointSize;
+ }
- // Make sure text last character is EOL
- text[textLength] = '\0';
- }
- else if ((textLength > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))))
+ else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger)))
{
- autoCursorDelayCounter++;
+ // Delete single codepoint from text, before current cursor position
- if (IsKeyPressed(KEY_BACKSPACE) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames
- {
- int prevCodepointSize = 0;
+ int prevCodepointSize = 0;
- // Prevent cursor index from decrementing past 0
- if (textBoxCursorIndex > 0)
- {
- GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+ GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
- // Move backward text from cursor position
- for (int i = (textBoxCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize];
+ // Move text after cursor forward (including final null terminator)
+ for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - prevCodepointSize] = text[i];
- textBoxCursorIndex -= codepointSize;
- textLength -= codepointSize;
- }
-
- // Make sure text last character is EOL
- text[textLength] = '\0';
- }
+ textLength -= prevCodepointSize;
+ textBoxCursorIndex -= prevCodepointSize;
}
// Move cursor position with keys
- if (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))
+ if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
{
- autoCursorDelayCounter++;
+ int offset = textBoxCursorIndex;
+ //int accCodepointSize = 0;
+ int prevCodepointSize;
+ int prevCodepoint;
- if (IsKeyPressed(KEY_LEFT) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames
+ // Check whitespace to skip (ASCII only)
+ while (offset > 0)
{
- int prevCodepointSize = 0;
- if (textBoxCursorIndex > 0) GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+ prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+ if (!isspace(prevCodepoint & 0xff)) break;
- if (textBoxCursorIndex >= prevCodepointSize) textBoxCursorIndex -= prevCodepointSize;
+ offset -= prevCodepointSize;
+ //accCodepointSize += prevCodepointSize;
}
+
+ // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
+ // Not using isalnum() since it only works on ASCII characters
+ bool puctuation = ispunct(prevCodepoint & 0xff);
+ while (offset > 0)
+ {
+ prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+ if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
+
+ offset -= prevCodepointSize;
+ //accCodepointSize += prevCodepointSize;
+ }
+
+ textBoxCursorIndex = offset;
}
- else if (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))
+ else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger)))
{
- autoCursorDelayCounter++;
+ int prevCodepointSize = 0;
+ GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
- if (IsKeyPressed(KEY_RIGHT) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames
+ textBoxCursorIndex -= prevCodepointSize;
+ }
+ else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+ {
+ int offset = textBoxCursorIndex;
+ //int accCodepointSize = 0;
+ int nextCodepointSize;
+ int nextCodepoint;
+
+ // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
+ // Not using isalnum() since it only works on ASCII characters
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+ bool puctuation = ispunct(nextCodepoint & 0xff);
+ while (offset < textLength)
{
- int nextCodepointSize = 0;
- GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+ if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) break;
- if ((textBoxCursorIndex + nextCodepointSize) <= textLength) textBoxCursorIndex += nextCodepointSize;
+ offset += nextCodepointSize;
+ //accCodepointSize += nextCodepointSize;
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
}
+
+ // Check whitespace to skip (ASCII only)
+ while (offset < textLength)
+ {
+ if (!isspace(nextCodepoint & 0xff)) break;
+
+ offset += nextCodepointSize;
+ //accCodepointSize += nextCodepointSize;
+ nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+ }
+
+ textBoxCursorIndex = offset;
+ }
+ else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger)))
+ {
+ int nextCodepointSize = 0;
+ GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+
+ textBoxCursorIndex += nextCodepointSize;
}
// Move cursor position with mouse
@@ -2701,7 +2827,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
float widthToMouseX = 0;
int mouseCursorIndex = 0;
- for (int i = textIndexOffset; i < textLength; i++)
+ for (int i = textIndexOffset; i < textLength; i += codepointSize)
{
codepoint = GetCodepointNext(&text[i], &codepointSize);
codepointIndex = GetGlyphIndex(guiFont, codepoint);
@@ -2720,7 +2846,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
}
// Check if mouse cursor is at the last position
- int textEndWidth = GetTextWidth(text + textIndexOffset);
+ int textEndWidth = GuiGetTextWidth(text + textIndexOffset);
if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2))
{
mouseCursor.x = textBounds.x + textEndWidth;
@@ -2737,7 +2863,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
else mouseCursor.x = -1;
// Recalculate cursor position.y depending on textBoxCursorIndex
- cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
+ cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
//if (multiline) cursor.y = GetTextLines()
// Finish text editing on ENTER or mouse click outside bounds
@@ -2745,6 +2871,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
(!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
{
textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index
+ autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes
result = 1;
}
}
@@ -2757,6 +2884,7 @@ int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
{
textBoxCursorIndex = textLength; // GLOBAL: Place cursor index to the end of current text
+ autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes
result = 1;
}
}
@@ -2825,19 +2953,22 @@ int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int
int tempValue = *value;
- Rectangle spinner = { bounds.x + GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SPIN_BUTTON_SPACING), bounds.y,
- bounds.width - 2*(GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SPIN_BUTTON_SPACING)), bounds.height };
- Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.height };
- Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.y, (float)GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.height };
+ Rectangle valueBoxBounds = {
+ bounds.x + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING),
+ bounds.y,
+ bounds.width - 2*(GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING)), bounds.height };
+ Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height };
+ Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.y,
+ (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height };
Rectangle textBounds = { 0 };
if (text != NULL)
{
- textBounds.width = (float)GetTextWidth(text) + 2;
+ textBounds.width = (float)GuiGetTextWidth(text) + 2;
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
- textBounds.x = bounds.x + bounds.width + GuiGetStyle(SPINNER, TEXT_PADDING);
+ textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
- if (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SPINNER, TEXT_PADDING);
+ if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
}
// Update control
@@ -2871,20 +3002,20 @@ int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int
// Draw control
//--------------------------------------------------------------------
- result = GuiValueBox(spinner, NULL, &tempValue, minValue, maxValue, editMode);
+ result = GuiValueBox(valueBoxBounds, NULL, &tempValue, minValue, maxValue, editMode);
// Draw value selector custom buttons
// NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
- GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(SPINNER, BORDER_WIDTH));
+ GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(VALUEBOX, BORDER_WIDTH));
GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
// Draw text label if provided
- GuiDrawText(text, textBounds, (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+ GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
//--------------------------------------------------------------------
*value = tempValue;
@@ -2902,13 +3033,13 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in
int result = 0;
GuiState state = guiState;
- char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0";
- sprintf(textValue, "%i", *value);
+ char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = { 0 };
+ snprintf(textValue, RAYGUI_VALUEBOX_MAX_CHARS + 1, "%i", *value);
Rectangle textBounds = { 0 };
if (text != NULL)
{
- textBounds.width = (float)GetTextWidth(text) + 2;
+ textBounds.width = (float)GuiGetTextWidth(text) + 2;
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -2920,7 +3051,6 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in
if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
{
Vector2 mousePoint = GetMousePosition();
-
bool valueHasChanged = false;
if (editMode)
@@ -2929,30 +3059,53 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in
int keyCount = (int)strlen(textValue);
- // Only allow keys in range [48..57]
- if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+ // Add or remove minus symbol
+ if (IsKeyPressed(KEY_MINUS))
{
- if (GetTextWidth(textValue) < bounds.width)
+ if (textValue[0] == '-')
{
- int key = GetCharPressed();
- if ((key >= 48) && (key <= 57))
+ for (int i = 0 ; i < keyCount; i++) textValue[i] = textValue[i + 1];
+
+ keyCount--;
+ valueHasChanged = true;
+ }
+ else if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+ {
+ if (keyCount == 0)
{
- textValue[keyCount] = (char)key;
+ textValue[0] = '0';
+ textValue[1] = '\0';
keyCount++;
- valueHasChanged = true;
}
+
+ for (int i = keyCount ; i > -1; i--) textValue[i + 1] = textValue[i];
+
+ textValue[0] = '-';
+ keyCount++;
+ valueHasChanged = true;
+ }
+ }
+
+ // Add new digit to text value
+ if ((keyCount >= 0) && (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) && (GuiGetTextWidth(textValue) < bounds.width))
+ {
+ int key = GetCharPressed();
+
+ // Only allow keys in range [48..57]
+ if ((key >= 48) && (key <= 57))
+ {
+ textValue[keyCount] = (char)key;
+ keyCount++;
+ valueHasChanged = true;
}
}
// Delete text
- if (keyCount > 0)
+ if ((keyCount > 0) && IsKeyPressed(KEY_BACKSPACE))
{
- if (IsKeyPressed(KEY_BACKSPACE))
- {
- keyCount--;
- textValue[keyCount] = '\0';
- valueHasChanged = true;
- }
+ keyCount--;
+ textValue[keyCount] = '\0';
+ valueHasChanged = true;
}
if (valueHasChanged) *value = TextToInteger(textValue);
@@ -2992,11 +3145,14 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in
GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor);
GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))));
- // Draw cursor
+ // Draw cursor rectangle
if (editMode)
{
// NOTE: ValueBox internal text is always centered
- Rectangle cursor = { bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 1, bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4, bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH) };
+ Rectangle cursor = { bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1,
+ bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + 2,
+ 2, bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2 - 4 };
+ if (cursor.height > bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2;
GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
}
@@ -3019,12 +3175,12 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float
GuiState state = guiState;
//char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0";
- //sprintf(textValue, "%2.2f", *value);
+ //snprintf(textValue, sizeof(textValue), "%2.2f", *value);
- Rectangle textBounds = {0};
+ Rectangle textBounds = { 0 };
if (text != NULL)
{
- textBounds.width = (float)GetTextWidth(text) + 2;
+ textBounds.width = (float)GuiGetTextWidth(text) + 2;
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -3045,10 +3201,37 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float
int keyCount = (int)strlen(textValue);
+ // Add or remove minus symbol
+ if (IsKeyPressed(KEY_MINUS))
+ {
+ if (textValue[0] == '-')
+ {
+ for (int i = 0; i < keyCount; i++) textValue[i] = textValue[i + 1];
+
+ keyCount--;
+ valueHasChanged = true;
+ }
+ else if (keyCount < (RAYGUI_VALUEBOX_MAX_CHARS - 1))
+ {
+ if (keyCount == 0)
+ {
+ textValue[0] = '0';
+ textValue[1] = '\0';
+ keyCount++;
+ }
+
+ for (int i = keyCount; i > -1; i--) textValue[i + 1] = textValue[i];
+
+ textValue[0] = '-';
+ keyCount++;
+ valueHasChanged = true;
+ }
+ }
+
// Only allow keys in range [48..57]
if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
{
- if (GetTextWidth(textValue) < bounds.width)
+ if (GuiGetTextWidth(textValue) < bounds.width)
{
int key = GetCharPressed();
if (((key >= 48) && (key <= 57)) ||
@@ -3103,7 +3286,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float
if (editMode)
{
// NOTE: ValueBox internal text is always centered
- Rectangle cursor = {bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 1,
+ Rectangle cursor = {bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1,
bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4,
bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH)};
GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
@@ -3120,7 +3303,7 @@ int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float
// Slider control with pro parameters
// NOTE: Other GuiSlider*() controls use this one
-int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth)
+int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
{
int result = 0;
GuiState state = guiState;
@@ -3129,6 +3312,8 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight,
if (value == NULL) value = &temp;
float oldValue = *value;
+ int sliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH);
+
Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) };
@@ -3146,7 +3331,7 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight,
{
state = STATE_PRESSED;
// Get equivalent value and slider position from mousePosition.x
- *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width-sliderWidth)) + minValue;
+ *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue;
}
}
else
@@ -3166,7 +3351,7 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight,
if (!CheckCollisionPointRec(mousePoint, slider))
{
// Get equivalent value and slider position from mousePosition.x
- *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width-sliderWidth)) + minValue;
+ *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue;
}
}
else state = STATE_FOCUSED;
@@ -3205,44 +3390,45 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight,
if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)));
else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)));
else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_PRESSED)));
+ else if (state == STATE_DISABLED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_DISABLED)));
// Draw left/right text if provided
if (textLeft != NULL)
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(textLeft);
+ textBounds.width = (float)GuiGetTextWidth(textLeft);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
- GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))));
+ GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
}
if (textRight != NULL)
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(textRight);
+ textBounds.width = (float)GuiGetTextWidth(textRight);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
- GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))));
+ GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
}
//--------------------------------------------------------------------
return result;
}
-// Slider control extended, returns selected value and has text
-int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
-{
- return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH));
-}
-
// Slider Bar control extended, returns selected value
int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
{
- return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, 0);
+ int result = 0;
+ int preSliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH);
+ GuiSetStyle(SLIDER, SLIDER_WIDTH, 0);
+ result = GuiSlider(bounds, textLeft, textRight, value, minValue, maxValue);
+ GuiSetStyle(SLIDER, SLIDER_WIDTH, preSliderWidth);
+
+ return result;
}
// Progress Bar control extended, shows current progress value
@@ -3257,14 +3443,14 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight
// Progress bar
Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH),
bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), 0,
- bounds.height - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) };
+ bounds.height - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) -1 };
// Update control
//--------------------------------------------------------------------
if (*value > maxValue) *value = maxValue;
// WARNING: Working with floats could lead to rounding issues
- if ((state != STATE_DISABLED)) progress.width = (float)(*value/(maxValue - minValue))*bounds.width - ((*value >= maxValue)? (float)(2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)) : 0.0f);
+ if ((state != STATE_DISABLED)) progress.width = ((float)*value/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH));
//--------------------------------------------------------------------
// Draw control
@@ -3282,15 +3468,15 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight
GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
}
- else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+ else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
- if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + 1, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+ if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1}, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
else
{
// Draw borders not yet reached by value
- GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + 1, bounds.y, bounds.width - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
- GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + 1, bounds.y + bounds.height - 1, bounds.width - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
- GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+ GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+ GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y + bounds.height - 1, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+ GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
}
// Draw slider internal progress bar (depends on state)
@@ -3301,23 +3487,23 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight
if (textLeft != NULL)
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(textLeft);
+ textBounds.width = (float)GuiGetTextWidth(textLeft);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x - textBounds.width - GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
- GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))));
+ GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
}
if (textRight != NULL)
{
Rectangle textBounds = { 0 };
- textBounds.width = (float)GetTextWidth(textRight);
+ textBounds.width = (float)GuiGetTextWidth(textRight);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
- GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))));
+ GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
}
//--------------------------------------------------------------------
@@ -3467,11 +3653,11 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd
// Draw visible items
for (int i = 0; ((i < visibleItems) && (text != NULL)); i++)
{
- GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK);
+ if (GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_NORMAL)) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK);
if (state == STATE_DISABLED)
{
- if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)));
+ if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)));
GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)));
}
@@ -3480,18 +3666,18 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd
if (((startIndex + i) == itemSelected) && (active != NULL))
{
// Draw item selected
- GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)));
+ GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)));
GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED)));
}
else if (((startIndex + i) == itemFocused)) // && (focus != NULL)) // NOTE: We want items focused, despite not returned!
{
// Draw item focused
- GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)));
+ GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)));
GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED)));
}
else
{
- // Draw item normal
+ // Draw item normal (no rectangle)
GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)));
}
}
@@ -3531,22 +3717,22 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd
return result;
}
-// Color Panel control - Color (RGBA) variant.
+// Color Panel control - Color (RGBA) variant
int GuiColorPanel(Rectangle bounds, const char *text, Color *color)
{
int result = 0;
Vector3 vcolor = { (float)color->r/255.0f, (float)color->g/255.0f, (float)color->b/255.0f };
Vector3 hsv = ConvertRGBtoHSV(vcolor);
- Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv.
+ Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv
GuiColorPanelHSV(bounds, text, &hsv);
- // Check if the hsv was changed, only then change the color.
- // This is required, because the Color->HSV->Color conversion has precision errors.
- // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value.
- // Otherwise GuiColorPanel would often modify it's color without user input.
- // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check.
+ // Check if the hsv was changed, only then change the color
+ // This is required, because the Color->HSV->Color conversion has precision errors
+ // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value
+ // Otherwise GuiColorPanel would often modify it's color without user input
+ // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check
if (hsv.x != prevHsv.x || hsv.y != prevHsv.y || hsv.z != prevHsv.z)
{
Vector3 rgb = ConvertHSVtoRGB(hsv);
@@ -3570,7 +3756,10 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
int result = 0;
GuiState state = guiState;
- Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT), (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 };
+ Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2,
+ (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW),
+ (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT),
+ (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 };
// Update control
//--------------------------------------------------------------------
@@ -3617,7 +3806,6 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
// Draw control
//--------------------------------------------------------------------
-
// Draw alpha bar: checked background
if (state != STATE_DISABLED)
{
@@ -3755,7 +3943,7 @@ int GuiColorPicker(Rectangle bounds, const char *text, Color *color)
Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height };
//Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) };
- // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used.
+ // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used
Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ (*color).r/255.0f, (*color).g/255.0f, (*color).b/255.0f });
GuiColorBarHue(boundsHue, NULL, &hsv.x);
@@ -3768,8 +3956,8 @@ int GuiColorPicker(Rectangle bounds, const char *text, Color *color)
return result;
}
-// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering.
-// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB.
+// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering
+// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB
// NOTE: It's divided in multiple controls:
// int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
// int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
@@ -3917,7 +4105,7 @@ int GuiMessageBox(Rectangle bounds, const char *title, const char *message, cons
buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount;
buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT;
- //int textWidth = GetTextWidth(message) + 2;
+ //int textWidth = GuiGetTextWidth(message) + 2;
Rectangle textBounds = { 0 };
textBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
@@ -3981,7 +4169,7 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co
Rectangle textBounds = { 0 };
if (message != NULL)
{
- int textSize = GetTextWidth(message) + 2;
+ int textSize = GuiGetTextWidth(message) + 2;
textBounds.x = bounds.x + bounds.width/2 - textSize/2;
textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + messageInputHeight/4 - (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -4221,7 +4409,7 @@ void GuiLoadStyle(const char *fileName)
if (fileDataSize > 0)
{
- unsigned char *fileData = (unsigned char *)RAYGUI_MALLOC(fileDataSize*sizeof(unsigned char));
+ unsigned char *fileData = (unsigned char *)RAYGUI_CALLOC(fileDataSize, sizeof(unsigned char));
fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile);
GuiLoadStyleFromMemory(fileData, fileDataSize);
@@ -4283,8 +4471,6 @@ void GuiLoadStyleDefault(void)
GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
GuiSetStyle(VALUEBOX, TEXT_PADDING, 0);
GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
- GuiSetStyle(SPINNER, TEXT_PADDING, 0);
- GuiSetStyle(SPINNER, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
GuiSetStyle(STATUSBAR, TEXT_PADDING, 8);
GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
@@ -4299,8 +4485,8 @@ void GuiLoadStyleDefault(void)
GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2);
GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16);
GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2);
- GuiSetStyle(SPINNER, SPIN_BUTTON_WIDTH, 24);
- GuiSetStyle(SPINNER, SPIN_BUTTON_SPACING, 2);
+ GuiSetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH, 24);
+ GuiSetStyle(VALUEBOX, SPINNER_BUTTON_SPACING, 2);
GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0);
GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0);
GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6);
@@ -4310,6 +4496,7 @@ void GuiLoadStyleDefault(void)
GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 12);
GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 28);
GuiSetStyle(LISTVIEW, LIST_ITEMS_SPACING, 2);
+ GuiSetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH, 1);
GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 12);
GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE);
GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 8);
@@ -4322,8 +4509,8 @@ void GuiLoadStyleDefault(void)
{
// Unload previous font texture
UnloadTexture(guiFont.texture);
- RL_FREE(guiFont.recs);
- RL_FREE(guiFont.glyphs);
+ RAYGUI_FREE(guiFont.recs);
+ RAYGUI_FREE(guiFont.glyphs);
guiFont.recs = NULL;
guiFont.glyphs = NULL;
@@ -4352,7 +4539,7 @@ const char *GuiIconText(int iconId, const char *text)
if (text != NULL)
{
memset(buffer, 0, 1024);
- sprintf(buffer, "#%03i#", iconId);
+ snprintf(buffer, 1024, "#%03i#", iconId);
for (int i = 5; i < 1024; i++)
{
@@ -4364,7 +4551,7 @@ const char *GuiIconText(int iconId, const char *text)
}
else
{
- sprintf(iconBuffer, "#%03i#", iconId);
+ snprintf(iconBuffer, 16, "#%03i#", iconId);
return iconBuffer;
}
@@ -4430,17 +4617,17 @@ char **GuiLoadIcons(const char *fileName, bool loadIconsName)
{
if (loadIconsName)
{
- guiIconsName = (char **)RAYGUI_MALLOC(iconCount*sizeof(char **));
+ guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *));
for (int i = 0; i < iconCount; i++)
{
- guiIconsName[i] = (char *)RAYGUI_MALLOC(RAYGUI_ICON_MAX_NAME_LENGTH);
+ guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char));
fread(guiIconsName[i], 1, RAYGUI_ICON_MAX_NAME_LENGTH, rgiFile);
}
}
else fseek(rgiFile, iconCount*RAYGUI_ICON_MAX_NAME_LENGTH, SEEK_CUR);
// Read icons data directly over internal icons array
- fread(guiIconsPtr, sizeof(unsigned int), iconCount*(iconSize*iconSize/32), rgiFile);
+ fread(guiIconsPtr, sizeof(unsigned int), (int)iconCount*((int)iconSize*(int)iconSize/32), rgiFile);
}
fclose(rgiFile);
@@ -4449,6 +4636,56 @@ char **GuiLoadIcons(const char *fileName, bool loadIconsName)
return guiIconsName;
}
+// Load icons from memory
+// WARNING: Binary files only
+char **GuiLoadIconsFromMemory(const unsigned char *fileData, int dataSize, bool loadIconsName)
+{
+ unsigned char *fileDataPtr = (unsigned char *)fileData;
+ char **guiIconsName = NULL;
+
+ char signature[5] = { 0 };
+ short version = 0;
+ short reserved = 0;
+ short iconCount = 0;
+ short iconSize = 0;
+
+ memcpy(signature, fileDataPtr, 4);
+ memcpy(&version, fileDataPtr + 4, sizeof(short));
+ memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short));
+ memcpy(&iconCount, fileDataPtr + 4 + 2 + 2, sizeof(short));
+ memcpy(&iconSize, fileDataPtr + 4 + 2 + 2 + 2, sizeof(short));
+ fileDataPtr += 12;
+
+ if ((signature[0] == 'r') &&
+ (signature[1] == 'G') &&
+ (signature[2] == 'I') &&
+ (signature[3] == ' '))
+ {
+ if (loadIconsName)
+ {
+ guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *));
+ for (int i = 0; i < iconCount; i++)
+ {
+ guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char));
+ memcpy(guiIconsName[i], fileDataPtr, RAYGUI_ICON_MAX_NAME_LENGTH);
+ fileDataPtr += RAYGUI_ICON_MAX_NAME_LENGTH;
+ }
+ }
+ else
+ {
+ // Skip icon name data if not required
+ fileDataPtr += iconCount*RAYGUI_ICON_MAX_NAME_LENGTH;
+ }
+
+ int iconDataSize = iconCount*((int)iconSize*(int)iconSize/32)*(int)sizeof(unsigned int);
+ guiIconsPtr = (unsigned int *)RAYGUI_CALLOC(iconDataSize, 1);
+
+ memcpy(guiIconsPtr, fileDataPtr, iconDataSize);
+ }
+
+ return guiIconsName;
+}
+
// Draw selected icon using rectangles pixel-by-pixel
void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color)
{
@@ -4476,12 +4713,73 @@ void GuiSetIconScale(int scale)
if (scale >= 1) guiIconScale = scale;
}
+// Get text width considering gui style and icon size (if required)
+int GuiGetTextWidth(const char *text)
+{
+ #if !defined(ICON_TEXT_PADDING)
+ #define ICON_TEXT_PADDING 4
+ #endif
+
+ Vector2 textSize = { 0 };
+ int textIconOffset = 0;
+
+ if ((text != NULL) && (text[0] != '\0'))
+ {
+ if (text[0] == '#')
+ {
+ for (int i = 1; (i < 5) && (text[i] != '\0'); i++)
+ {
+ if (text[i] == '#')
+ {
+ textIconOffset = i;
+ break;
+ }
+ }
+ }
+
+ text += textIconOffset;
+
+ // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly
+ float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+
+ // Custom MeasureText() implementation
+ if ((guiFont.texture.id > 0) && (text != NULL))
+ {
+ // Get size in bytes of text, considering end of line and line break
+ int size = 0;
+ for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++)
+ {
+ if ((text[i] != '\0') && (text[i] != '\n')) size++;
+ else break;
+ }
+
+ float scaleFactor = fontSize/(float)guiFont.baseSize;
+ textSize.y = (float)guiFont.baseSize*scaleFactor;
+ float glyphWidth = 0.0f;
+
+ for (int i = 0, codepointSize = 0; i < size; i += codepointSize)
+ {
+ int codepoint = GetCodepointNext(&text[i], &codepointSize);
+ int codepointIndex = GetGlyphIndex(guiFont, codepoint);
+
+ if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
+ else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
+
+ textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+ }
+ }
+
+ if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING);
+ }
+
+ return (int)textSize.x;
+}
+
#endif // !RAYGUI_NO_ICONS
//----------------------------------------------------------------------------------
// Module Internal Functions Definition
//----------------------------------------------------------------------------------
-
// Load style from memory
// WARNING: Binary files only
static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
@@ -4567,7 +4865,7 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
{
// Compressed font atlas image data (DEFLATE), it requires DecompressData()
int dataUncompSize = 0;
- unsigned char *compData = (unsigned char *)RAYGUI_MALLOC(fontImageCompSize);
+ unsigned char *compData = (unsigned char *)RAYGUI_CALLOC(fontImageCompSize, sizeof(unsigned char));
memcpy(compData, fileDataPtr, fontImageCompSize);
fileDataPtr += fontImageCompSize;
@@ -4581,7 +4879,7 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
else
{
// Font atlas image data is not compressed
- imFont.data = (unsigned char *)RAYGUI_MALLOC(fontImageUncompSize);
+ imFont.data = (unsigned char *)RAYGUI_CALLOC(fontImageUncompSize, sizeof(unsigned char));
memcpy(imFont.data, fileDataPtr, fontImageUncompSize);
fileDataPtr += fontImageUncompSize;
}
@@ -4609,7 +4907,7 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
if ((recsDataCompressedSize > 0) && (recsDataCompressedSize != recsDataSize))
{
// Recs data is compressed, uncompress it
- unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_MALLOC(recsDataCompressedSize);
+ unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_CALLOC(recsDataCompressedSize, sizeof(unsigned char));
memcpy(recsDataCompressed, fileDataPtr, recsDataCompressedSize);
fileDataPtr += recsDataCompressedSize;
@@ -4651,7 +4949,7 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
if ((glyphsDataCompressedSize > 0) && (glyphsDataCompressedSize != glyphsDataSize))
{
// Glyphs data is compressed, uncompress it
- unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_MALLOC(glyphsDataCompressedSize);
+ unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_CALLOC(glyphsDataCompressedSize, sizeof(unsigned char));
memcpy(glypsDataCompressed, fileDataPtr, glyphsDataCompressedSize);
fileDataPtr += glyphsDataCompressedSize;
@@ -4704,68 +5002,6 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
}
}
-// Gui get text width considering icon
-static int GetTextWidth(const char *text)
-{
- #if !defined(ICON_TEXT_PADDING)
- #define ICON_TEXT_PADDING 4
- #endif
-
- Vector2 textSize = { 0 };
- int textIconOffset = 0;
-
- if ((text != NULL) && (text[0] != '\0'))
- {
- if (text[0] == '#')
- {
- for (int i = 1; (i < 5) && (text[i] != '\0'); i++)
- {
- if (text[i] == '#')
- {
- textIconOffset = i;
- break;
- }
- }
- }
-
- text += textIconOffset;
-
- // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly
- float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
-
- // Custom MeasureText() implementation
- if ((guiFont.texture.id > 0) && (text != NULL))
- {
- // Get size in bytes of text, considering end of line and line break
- int size = 0;
- for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++)
- {
- if ((text[i] != '\0') && (text[i] != '\n')) size++;
- else break;
- }
-
- float scaleFactor = fontSize/(float)guiFont.baseSize;
- textSize.y = (float)guiFont.baseSize*scaleFactor;
- float glyphWidth = 0.0f;
-
- for (int i = 0, codepointSize = 0; i < size; i += codepointSize)
- {
- int codepoint = GetCodepointNext(&text[i], &codepointSize);
- int codepointIndex = GetGlyphIndex(guiFont, codepoint);
-
- if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
- else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
-
- textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
- }
- }
-
- if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING);
- }
-
- return (int)textSize.x;
-}
-
// Get text bounds considering control bounds
static Rectangle GetTextBounds(int control, Rectangle bounds)
{
@@ -4786,7 +5022,7 @@ static Rectangle GetTextBounds(int control, Rectangle bounds)
case SLIDER:
case CHECKBOX:
case VALUEBOX:
- case SPINNER:
+ case CONTROL11:
// TODO: More special cases (label on side): SLIDER, CHECKBOX, VALUEBOX, SPINNER
default:
{
@@ -4832,7 +5068,8 @@ static const char *GetTextIcon(const char *text, int *iconId)
}
// Get text divided into lines (by line-breaks '\n')
-const char **GetTextLines(const char *text, int *count)
+// WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator!
+static const char **GetTextLines(const char *text, int *count)
{
#define RAYGUI_MAX_TEXT_LINES 128
@@ -4842,25 +5079,18 @@ const char **GetTextLines(const char *text, int *count)
int textSize = (int)strlen(text);
lines[0] = text;
- int len = 0;
*count = 1;
- //int lineSize = 0; // Stores current line size, not returned
for (int i = 0, k = 0; (i < textSize) && (*count < RAYGUI_MAX_TEXT_LINES); i++)
{
if (text[i] == '\n')
{
- //lineSize = len;
k++;
- lines[k] = &text[i + 1]; // WARNING: next value is valid?
- len = 0;
+ lines[k] = &text[i + 1]; // WARNING: next value is valid?
*count += 1;
}
- else len++;
}
- //lines[*count - 1].size = len;
-
return lines;
}
@@ -4936,8 +5166,8 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C
float textBoundsWidthOffset = 0.0f;
// NOTE: We get text size after icon has been processed
- // WARNING: GetTextWidth() also processes text icon to get width! -> Really needed?
- int textSizeX = GetTextWidth(lines[i]);
+ // WARNING: GuiGetTextWidth() also processes text icon to get width! -> Really needed?
+ int textSizeX = GuiGetTextWidth(lines[i]);
// If text requires an icon, add size to measure
if (iconId >= 0)
@@ -5000,7 +5230,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C
float textOffsetX = 0.0f;
float glyphWidth = 0;
- int ellipsisWidth = GetTextWidth("...");
+ int ellipsisWidth = GuiGetTextWidth("...");
bool textOverflow = false;
for (int c = 0, codepointSize = 0; c < lineSize; c += codepointSize)
{
@@ -5144,13 +5374,13 @@ static void GuiTooltip(Rectangle controlRec)
if ((controlRec.x + textSize.x + 16) > GetScreenWidth()) controlRec.x -= (textSize.x + 16 - controlRec.width);
- GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.f }, NULL);
+ GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, NULL);
int textPadding = GuiGetStyle(LABEL, TEXT_PADDING);
int textAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
GuiSetStyle(LABEL, TEXT_PADDING, 0);
GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
- GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.f }, guiTooltipPtr);
+ GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, guiTooltipPtr);
GuiSetStyle(LABEL, TEXT_ALIGNMENT, textAlignment);
GuiSetStyle(LABEL, TEXT_PADDING, textPadding);
}
@@ -5204,7 +5434,7 @@ static const char **GuiTextSplit(const char *text, char delimiter, int *count, i
buffer[i] = '\0'; // Set an end of string at this point
counter++;
- if (counter > RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
+ if (counter >= RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
}
}
@@ -5526,10 +5756,10 @@ static Color GetColor(int hexValue)
{
Color color;
- color.r = (unsigned char)(hexValue >> 24) & 0xFF;
- color.g = (unsigned char)(hexValue >> 16) & 0xFF;
- color.b = (unsigned char)(hexValue >> 8) & 0xFF;
- color.a = (unsigned char)hexValue & 0xFF;
+ color.r = (unsigned char)(hexValue >> 24) & 0xff;
+ color.g = (unsigned char)(hexValue >> 16) & 0xff;
+ color.b = (unsigned char)(hexValue >> 8) & 0xff;
+ color.a = (unsigned char)hexValue & 0xff;
return color;
}
@@ -5562,7 +5792,7 @@ static const char *TextFormat(const char *text, ...)
va_list args;
va_start(args, text);
- vsprintf(buffer, text, args);
+ vsnprintf(buffer, RAYGUI_TEXTFORMAT_MAX_SIZE, text, args);
va_end(args);
return buffer;
@@ -5731,7 +5961,7 @@ static int GetCodepointNext(const char *text, int *codepointSize)
}
else if (0xe0 == (0xf0 & ptr[0]))
{
- // 3 byte UTF-8 codepoint */
+ // 3 byte UTF-8 codepoint
if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]);
*codepointSize = 3;
diff --git a/examples/shapes/shapes_ball_physics.c b/examples/shapes/shapes_ball_physics.c
new file mode 100644
index 000000000..f9b620d28
--- /dev/null
+++ b/examples/shapes/shapes_ball_physics.c
@@ -0,0 +1,221 @@
+/*******************************************************************************************
+*
+* raylib [shapes] example - ball physics
+*
+* Example complexity rating: [★★☆☆] 2/4
+*
+* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+* Example contributed by David Buzatto (@davidbuzatto) 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 David Buzatto (@davidbuzatto)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include
+#include
+
+#define MAX_BALLS 5000 // Maximum quantity of balls
+
+typedef struct Ball {
+ Vector2 pos; // Position
+ Vector2 vel; // Velocity
+ Vector2 ppos; // Previous position
+ float radius;
+ float friction;
+ float elasticity;
+ Color color;
+ bool grabbed;
+} Ball;
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [shapes] example - ball physics");
+
+ Ball balls[MAX_BALLS] = {{
+ .pos = { GetScreenWidth()/2, GetScreenHeight()/2 },
+ .vel = { 200, 200 },
+ .ppos = { 0 },
+ .radius = 40,
+ .friction = 0.99,
+ .elasticity = 0.9,
+ .color = BLUE,
+ .grabbed = false
+ }};
+
+ int ballCount = 1;
+ Ball *grabbedBall = NULL; // A pointer to the current ball that is grabbed
+ Vector2 pressOffset = {0}; // Mouse press offset relative to the ball that grabbedd
+
+ float gravity = 100; // World gravity
+
+ 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
+ //----------------------------------------------------------------------------------
+ float delta = GetFrameTime();
+ Vector2 mousePos = GetMousePosition();
+
+ // Checks if a ball was grabbed
+ if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
+ {
+ for (int i = ballCount - 1; i >= 0; i--)
+ {
+ Ball *ball = &balls[i];
+ pressOffset.x = mousePos.x - ball->pos.x;
+ pressOffset.y = mousePos.y - ball->pos.y;
+
+ // If the distance between the ball position and the mouse press position
+ // is less than or equal to the ball radius, the event occurred inside the ball
+ if (hypot(pressOffset.x, pressOffset.y) <= ball->radius)
+ {
+ ball->grabbed = true;
+ grabbedBall = ball;
+ break;
+ }
+ }
+ }
+
+ // Releases any ball the was grabbed
+ if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT))
+ {
+ if (grabbedBall != NULL)
+ {
+ grabbedBall->grabbed = false;
+ grabbedBall = NULL;
+ }
+ }
+
+ // Creates a new ball
+ if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT) || (IsKeyDown(KEY_LEFT_CONTROL) && IsMouseButtonDown(MOUSE_BUTTON_RIGHT)))
+ {
+ if (ballCount < MAX_BALLS)
+ {
+ balls[ballCount++] = (Ball){
+ .pos = mousePos,
+ .vel = { GetRandomValue(-300, 300), GetRandomValue(-300, 300) },
+ .ppos = { 0 },
+ .radius = 20 + GetRandomValue(0, 30),
+ .friction = 0.99,
+ .elasticity = 0.9,
+ .color = { GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 },
+ .grabbed = false
+ };
+ }
+ }
+
+ // Shake balls
+ if (IsMouseButtonPressed(MOUSE_BUTTON_MIDDLE))
+ {
+ for (int i = 0; i < ballCount; i++)
+ {
+ if (!balls[i].grabbed) balls[i].vel = (Vector2){ GetRandomValue(-2000, 2000), GetRandomValue(-2000, 2000) };
+ }
+ }
+
+ // Changes gravity
+ gravity += GetMouseWheelMove()*5;
+
+ // Updates each ball state
+ for (int i = 0; i < ballCount; i++)
+ {
+ Ball *ball = &balls[i];
+
+ // The ball is not grabbed
+ if (!ball->grabbed)
+ {
+ // Ball repositioning using the velocity
+ ball->pos.x += ball->vel.x * delta;
+ ball->pos.y += ball->vel.y * delta;
+
+ // Does the ball hit the screen right boundary?
+ if ((ball->pos.x + ball->radius) >= screenWidth)
+ {
+ ball->pos.x = screenWidth - ball->radius; // Ball repositioning
+ ball->vel.x = -ball->vel.x*ball->elasticity; // Elasticity makes the ball lose 10% of its velocity on hit
+ }
+ // Does the ball hit the screen left boundary?
+ else if ((ball->pos.x - ball->radius) <= 0)
+ {
+ ball->pos.x = ball->radius;
+ ball->vel.x = -ball->vel.x*ball->elasticity;
+ }
+
+ // The same for y axis
+ if ((ball->pos.y + ball->radius) >= screenHeight)
+ {
+ ball->pos.y = screenHeight - ball->radius;
+ ball->vel.y = -ball->vel.y*ball->elasticity;
+ }
+ else if ((ball->pos.y - ball->radius) <= 0)
+ {
+ ball->pos.y = ball->radius;
+ ball->vel.y = -ball->vel.y*ball->elasticity;
+ }
+
+ // Friction makes the ball lose 1% of its velocity each frame
+ ball->vel.x = ball->vel.x*ball->friction;
+ // Gravity affects only the y axis
+ ball->vel.y = ball->vel.y*ball->friction + gravity;
+ }
+ else
+ {
+ // Ball repositioning using the mouse position
+ ball->pos.x = mousePos.x - pressOffset.x;
+ ball->pos.y = mousePos.y - pressOffset.y;
+
+ // While the ball is grabbed, recalculates its velocity
+ ball->vel.x = (ball->pos.x - ball->ppos.x)/delta;
+ ball->vel.y = (ball->pos.y - ball->ppos.y)/delta;
+ ball->ppos = ball->pos;
+ }
+ }
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ for (int i = 0; i < ballCount; i++)
+ {
+ DrawCircleV(balls[i].pos, balls[i].radius, balls[i].color);
+ DrawCircleLinesV(balls[i].pos, balls[i].radius, BLACK);
+ }
+
+ DrawText("grab a ball by pressing with the mouse and throw it by releasing", 10, 10, 10, DARKGRAY);
+ DrawText("right click to create new balls (keep left control pressed to create a lot)", 10, 30, 10, DARKGRAY);
+ DrawText("use mouse wheel to change gravity", 10, 50, 10, DARKGRAY);
+ DrawText("middle click to shake", 10, 70, 10, DARKGRAY);
+ DrawText(TextFormat("BALL COUNT: %d", ballCount), 10, GetScreenHeight() - 70, 20, BLACK);
+ DrawText(TextFormat("GRAVITY: %.2f", gravity), 10, GetScreenHeight() - 40, 20, BLACK);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
\ No newline at end of file
diff --git a/examples/shapes/shapes_ball_physics.png b/examples/shapes/shapes_ball_physics.png
new file mode 100644
index 000000000..89493275c
Binary files /dev/null and b/examples/shapes/shapes_ball_physics.png differ
diff --git a/examples/shapes/shapes_bouncing_ball.c b/examples/shapes/shapes_bouncing_ball.c
index 5ae699944..d55c47f56 100644
--- a/examples/shapes/shapes_bouncing_ball.c
+++ b/examples/shapes/shapes_bouncing_ball.c
@@ -7,7 +7,7 @@
* Example originally created with raylib 2.5, last time updated with raylib 2.5
*
* Example contributed by Ramon Santamaria (@raysan5), reviewed by Jopestpe (@jopestpe)
-*
+*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
@@ -49,14 +49,14 @@ int main(void)
//-----------------------------------------------------
if (IsKeyPressed(KEY_G)) useGravity = !useGravity;
if (IsKeyPressed(KEY_SPACE)) pause = !pause;
-
+
if (!pause)
{
ballPosition.x += ballSpeed.x;
ballPosition.y += ballSpeed.y;
if (useGravity) ballSpeed.y += gravity;
-
+
// Check walls collision for bouncing
if ((ballPosition.x >= (GetScreenWidth() - ballRadius)) || (ballPosition.x <= ballRadius)) ballSpeed.x *= -1.0f;
if ((ballPosition.y >= (GetScreenHeight() - ballRadius)) || (ballPosition.y <= ballRadius)) ballSpeed.y *= -0.95f;
@@ -72,7 +72,7 @@ int main(void)
DrawCircleV(ballPosition, (float)ballRadius, MAROON);
DrawText("PRESS SPACE to PAUSE BALL MOVEMENT", 10, GetScreenHeight() - 25, 20, LIGHTGRAY);
-
+
if (useGravity) DrawText("GRAVITY: ON (Press G to disable)", 10, GetScreenHeight() - 50, 20, DARKGREEN);
else DrawText("GRAVITY: OFF (Press G to enable)", 10, GetScreenHeight() - 50, 20, RED);
@@ -80,7 +80,7 @@ int main(void)
if (pause && ((framesCounter/30)%2)) DrawText("PAUSED", 350, 200, 30, GRAY);
DrawFPS(10, 10);
-
+
EndDrawing();
//-----------------------------------------------------
}
diff --git a/examples/shapes/shapes_bullet_hell.c b/examples/shapes/shapes_bullet_hell.c
index 95abc4dc9..2ff3be14b 100644
--- a/examples/shapes/shapes_bullet_hell.c
+++ b/examples/shapes/shapes_bullet_hell.c
@@ -107,7 +107,7 @@ int main(void)
float bulletDirection = baseDirection + (degreesPerRow*row);
- // Bullet speed * bullet direction, this will determine how much pixels will be incremented/decremented
+ // Bullet speed*bullet direction, this will determine how much pixels will be incremented/decremented
// from the bullet position every frame. Since the bullets doesn't change its direction and speed,
// only need to calculate it at the spawning time
// 0 degrees = right, 90 degrees = down, 180 degrees = left and 270 degrees = up, basically clockwise
diff --git a/examples/shapes/shapes_clock_of_clocks.c b/examples/shapes/shapes_clock_of_clocks.c
index 3fc24aa92..d87ec6411 100644
--- a/examples/shapes/shapes_clock_of_clocks.c
+++ b/examples/shapes/shapes_clock_of_clocks.c
@@ -14,9 +14,6 @@
* Copyright (c) 2025 JP Mortiboys (@themushroompirates)
*
********************************************************************************************/
-#if defined(WIN32)
-#define _CRT_SECURE_NO_WARNINGS
-#endif
#include "raylib.h"
@@ -35,14 +32,14 @@ int main(void)
SetConfigFlags(FLAG_MSAA_4X_HINT);
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - clock of clocks");
-
+
const Color bgColor = ColorLerp(DARKBLUE, BLACK, 0.75f);
const Color handsColor = ColorLerp(YELLOW, RAYWHITE, .25f);
-
+
const float clockFaceSize = 24;
const float clockFaceSpacing = 8.0f;
const float sectionSpacing = 16.0f;
-
+
const Vector2 TL = (Vector2){ 0.0f, 90.0f }; // Top-left corner
const Vector2 TR = (Vector2){ 90.0f, 180.0f }; // Top-right corner
const Vector2 BR = (Vector2){ 180.0f, 270.0f }; // Bottom-right corner
@@ -50,7 +47,7 @@ int main(void)
const Vector2 HH = (Vector2){ 0.0f, 180.0f }; // Horizontal line
const Vector2 VV = (Vector2){ 90.0f, 270.0f }; // Vertical line
const Vector2 ZZ = (Vector2){ 135.0f, 135.0f }; // Not relevant
-
+
const Vector2 digitAngles[10][24] = {
/* 0 */ { TL,HH,HH,TR, /* */ VV,TL,TR,VV,/* */ VV,VV,VV,VV,/* */ VV,VV,VV,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,HH,BR },
/* 1 */ { TL,HH,TR,ZZ, /* */ BL,TR,VV,ZZ,/* */ ZZ,VV,VV,ZZ,/* */ ZZ,VV,VV,ZZ,/* */ TL,BR,BL,TR,/* */ BL,HH,HH,BR },
@@ -63,24 +60,16 @@ int main(void)
/* 8 */ { TL,HH,HH,TR, /* */ VV,TL,TR,VV,/* */ VV,BL,BR,VV,/* */ VV,TL,TR,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,HH,BR },
/* 9 */ { TL,HH,HH,TR, /* */ VV,TL,TR,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,TR,VV,/* */ TL,HH,BR,VV,/* */ BL,HH,HH,BR },
};
+
// Time for the hands to move to the new position (in seconds); this must be <1s
- const float handsMoveDuration = .5f;
-
- // We store the previous seconds value so we can see if the time has changed
+ const float handsMoveDuration = 0.5f;
+
int prevSeconds = -1;
-
- // This represents the real position where the hands are right now
Vector2 currentAngles[6][24] = { 0 };
-
- // This is the position where the hands were moving from
Vector2 srcAngles[6][24] = { 0 };
- // This is the position where the hands are moving to
Vector2 dstAngles[6][24] = { 0 };
-
- // Current animation timer
+
float handsMoveTimer = 0.0f;
-
- // 12 or 24 hour mode
int hourMode = 24;
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
@@ -91,77 +80,64 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
-
// Get the current time
time_t rawtime;
struct tm *timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
-
- if (timeinfo->tm_sec != prevSeconds) {
+
+ if (timeinfo->tm_sec != prevSeconds)
+ {
// The time has changed, so we need to move the hands to the new positions
prevSeconds = timeinfo->tm_sec;
-
+
// Format the current time so we can access the individual digits
- const char *clockDigits = TextFormat("%02d%02d%02d", timeinfo->tm_hour % hourMode, timeinfo->tm_min, timeinfo->tm_sec);
-
+ const char *clockDigits = TextFormat("%02d%02d%02d", timeinfo->tm_hour%hourMode, timeinfo->tm_min, timeinfo->tm_sec);
+
// Fetch where we want all the hands to be
- for (int digit = 0; digit < 6; digit++) {
- for (int cell = 0; cell < 24; cell++) {
+ for (int digit = 0; digit < 6; digit++)
+ {
+ for (int cell = 0; cell < 24; cell++)
+ {
srcAngles[digit][cell] = currentAngles[digit][cell];
- dstAngles[digit][cell] = digitAngles[ clockDigits[digit] - '0' ][cell];
-
+ dstAngles[digit][cell] = digitAngles[clockDigits[digit] - '0'][cell];
+
// Quick exception for 12h mode
- if (digit == 0 && hourMode == 12 && clockDigits[0] == '0') {
- dstAngles[digit][cell] = ZZ;
- }
-
- if (srcAngles[digit][cell].x > dstAngles[digit][cell].x) {
- srcAngles[digit][cell].x -= 360.0f;
- }
- if (srcAngles[digit][cell].y > dstAngles[digit][cell].y) {
- srcAngles[digit][cell].y -= 360.0f;
- }
+ if ((digit == 0) && (hourMode == 12) && (clockDigits[0] == '0')) dstAngles[digit][cell] = ZZ;
+ if (srcAngles[digit][cell].x > dstAngles[digit][cell].x) srcAngles[digit][cell].x -= 360.0f;
+ if (srcAngles[digit][cell].y > dstAngles[digit][cell].y) srcAngles[digit][cell].y -= 360.0f;
}
}
-
+
// Reset the timer
handsMoveTimer = -GetFrameTime();
}
-
+
// Now let's animate all the hands if we need to
- if (handsMoveTimer < handsMoveDuration) {
+ if (handsMoveTimer < handsMoveDuration)
+ {
// Increase the timer but don't go above the maximum
handsMoveTimer = Clamp(handsMoveTimer + GetFrameTime(), 0, handsMoveDuration);
-
- // Calculate the % completion of the animation
- float t = handsMoveTimer / handsMoveDuration;
-
+
+ // Calculate the%completion of the animation
+ float t = handsMoveTimer/handsMoveDuration;
+
// A little cheeky smoothstep
- t = t * t * (3.0f - 2.0f * t);
-
- for (int digit = 0; digit < 6; digit++) {
- for (int cell = 0; cell < 24; cell++) {
+ t = t*t*(3.0f - 2.0f*t);
+
+ for (int digit = 0; digit < 6; digit++)
+ {
+ for (int cell = 0; cell < 24; cell++)
+ {
currentAngles[digit][cell].x = Lerp(srcAngles[digit][cell].x, dstAngles[digit][cell].x, t);
currentAngles[digit][cell].y = Lerp(srcAngles[digit][cell].y, dstAngles[digit][cell].y, t);
}
}
-
- if (handsMoveTimer == handsMoveDuration) {
- // The animation has now finished
- }
}
-
+
// Handle input
-
- // Toggle between 12 and 24 hour mode with space
- if (IsKeyPressed(KEY_SPACE)) {
- hourMode = 36 - hourMode;
- }
-
-
-
+ if (IsKeyPressed(KEY_SPACE)) hourMode = 36 - hourMode; // Toggle between 12 and 24 hour mode with space
//----------------------------------------------------------------------------------
// Draw
@@ -169,52 +145,52 @@ int main(void)
BeginDrawing();
ClearBackground(bgColor);
-
+
DrawText(TextFormat("%d-h mode, space to change", hourMode), 10, 30, 20, RAYWHITE);
-
+
float xOffset = 4.0f;
-
- for (int digit = 0; digit < 6; digit++) {
-
- for (int row = 0; row < 6; row++) {
- for (int col = 0; col < 4; col++) {
+
+ for (int digit = 0; digit < 6; digit++)
+ {
+ for (int row = 0; row < 6; row++)
+ {
+ for (int col = 0; col < 4; col++)
+ {
Vector2 centre = (Vector2){
- xOffset + col*(clockFaceSize+clockFaceSpacing) + clockFaceSize * .5f,
- 100 + row*(clockFaceSize+clockFaceSpacing) + clockFaceSize * .5f
+ xOffset + col*(clockFaceSize+clockFaceSpacing) + clockFaceSize*0.5f,
+ 100 + row*(clockFaceSize+clockFaceSpacing) + clockFaceSize*0.5f
};
- DrawRing(centre, clockFaceSize * 0.5f - 2.0f, clockFaceSize * 0.5f, 0, 360, 24, DARKGRAY);
-
+
+ DrawRing(centre, clockFaceSize*0.5f - 2.0f, clockFaceSize*0.5f, 0, 360, 24, DARKGRAY);
+
// Big hand
DrawRectanglePro(
- (Rectangle){centre.x, centre.y, clockFaceSize*.5f+4.0f, 4.0f},
+ (Rectangle){centre.x, centre.y, clockFaceSize*0.5f+4.0f, 4.0f},
(Vector2){ 2.0f, 2.0f },
currentAngles[digit][row*4+col].x,
handsColor
);
-
+
// Little hand
DrawRectanglePro(
- (Rectangle){centre.x, centre.y, clockFaceSize*.5f+2.0f, 4.0f},
+ (Rectangle){centre.x, centre.y, clockFaceSize*0.5f+2.0f, 4.0f},
(Vector2){ 2.0f, 2.0f },
currentAngles[digit][row*4+col].y,
handsColor
);
}
}
-
- xOffset += (clockFaceSize+clockFaceSpacing) * 4;
- if (digit % 2 == 1) {
-
+
+ xOffset += (clockFaceSize+clockFaceSpacing)*4;
+ if (digit%2 == 1)
+ {
DrawRing((Vector2){xOffset + 4.0f, 160.0f}, 6.0f, 8.0f, 0.0f, 360.0f, 24, handsColor);
DrawRing((Vector2){xOffset + 4.0f, 225.0f}, 6.0f, 8.0f, 0.0f, 360.0f, 24, handsColor);
-
xOffset += sectionSpacing;
-
}
}
-
+
DrawFPS(10, 10);
-
EndDrawing();
//----------------------------------------------------------------------------------
@@ -222,7 +198,6 @@ int main(void)
// De-Initialization
//--------------------------------------------------------------------------------------
-
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
diff --git a/examples/shapes/shapes_digital_clock.c b/examples/shapes/shapes_digital_clock.c
index 77b020704..cca3f3c44 100644
--- a/examples/shapes/shapes_digital_clock.c
+++ b/examples/shapes/shapes_digital_clock.c
@@ -116,7 +116,7 @@ int main(void)
ClearBackground(RAYWHITE);
// Draw clock in selected mode
- if (clockMode == CLOCK_ANALOG) DrawClockAnalog(clock, (Vector2){ 400, 240 });
+ if (clockMode == CLOCK_ANALOG) DrawClockAnalog(clock, (Vector2){ 400, 240 });
else if (clockMode == CLOCK_DIGITAL)
{
DrawClockDigital(clock, (Vector2){ 30, 60 });
@@ -128,7 +128,7 @@ int main(void)
DrawText(clockTime, GetScreenWidth()/2 - MeasureText(clockTime, 150)/2, 300, 150, BLACK);
}
- DrawText(TextFormat("Press [SPACE] to switch clock mode: %s",
+ DrawText(TextFormat("Press [SPACE] to switch clock mode: %s",
(clockMode == CLOCK_DIGITAL)? "DIGITAL CLOCK" : "ANALOGUE CLOCK"), 10, 10, 20, DARKGRAY);
EndDrawing();
@@ -183,13 +183,13 @@ static void DrawClockAnalog(Clock clock, Vector2 position)
// Draw clock minutes/seconds lines
for (int i = 0; i < 60; i++)
{
- DrawLineEx((Vector2){ position.x + (clock.second.length + ((i%5)? 10 : 6))*cosf((6.0f*i - 90.0f)*DEG2RAD),
- position.y + (clock.second.length + ((i%5)? 10 : 6))*sinf((6.0f*i - 90.0f)*DEG2RAD) },
- (Vector2){ position.x + (clock.second.length + 20)*cosf((6.0f*i - 90.0f)*DEG2RAD),
+ DrawLineEx((Vector2){ position.x + (clock.second.length + ((i%5)? 10 : 6))*cosf((6.0f*i - 90.0f)*DEG2RAD),
+ position.y + (clock.second.length + ((i%5)? 10 : 6))*sinf((6.0f*i - 90.0f)*DEG2RAD) },
+ (Vector2){ position.x + (clock.second.length + 20)*cosf((6.0f*i - 90.0f)*DEG2RAD),
position.y + (clock.second.length + 20)*sinf((6.0f*i - 90.0f)*DEG2RAD) }, ((i%5)? 1.0f : 3.0f), DARKGRAY);
-
+
// Draw seconds numbers
- //DrawText(TextFormat("%02i", i), centerPosition.x + (clock.second.length + 50)*cosf((6.0f*i - 90.0f)*DEG2RAD) - 10/2,
+ //DrawText(TextFormat("%02i", i), centerPosition.x + (clock.second.length + 50)*cosf((6.0f*i - 90.0f)*DEG2RAD) - 10/2,
// centerPosition.y + (clock.second.length + 50)*sinf((6.0f*i - 90.0f)*DEG2RAD) - 10/2, 10, GRAY);
}
@@ -256,25 +256,25 @@ static void Draw7SDisplay(Vector2 position, char segments, Color colorOn, Color
float offsetYAdjust = segmentThick*0.3f; // HACK: Adjust gap space between segment limits
// Segment A
- DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen/2.0f, position.y + segmentThick },
+ DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen/2.0f, position.y + segmentThick },
segmentLen, segmentThick, false, (segments & 0b00000001)? colorOn : colorOff);
// Segment B
- DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen + segmentThick/2.0f, position.y + 2*segmentThick + segmentLen/2.0f - offsetYAdjust },
+ DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen + segmentThick/2.0f, position.y + 2*segmentThick + segmentLen/2.0f - offsetYAdjust },
segmentLen, segmentThick, true, (segments & 0b00000010)? colorOn : colorOff);
// Segment C
- DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen + segmentThick/2.0f, position.y + 4*segmentThick + segmentLen + segmentLen/2.0f - 3*offsetYAdjust },
+ DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen + segmentThick/2.0f, position.y + 4*segmentThick + segmentLen + segmentLen/2.0f - 3*offsetYAdjust },
segmentLen, segmentThick, true, (segments & 0b00000100)? colorOn : colorOff);
// Segment D
- DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen/2.0f, position.y + 5*segmentThick + 2*segmentLen - 4*offsetYAdjust },
+ DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen/2.0f, position.y + 5*segmentThick + 2*segmentLen - 4*offsetYAdjust },
segmentLen, segmentThick, false, (segments & 0b00001000)? colorOn : colorOff);
// Segment E
- DrawDisplaySegment((Vector2){ position.x + segmentThick/2.0f, position.y + 4*segmentThick + segmentLen + segmentLen/2.0f - 3*offsetYAdjust },
+ DrawDisplaySegment((Vector2){ position.x + segmentThick/2.0f, position.y + 4*segmentThick + segmentLen + segmentLen/2.0f - 3*offsetYAdjust },
segmentLen, segmentThick, true, (segments & 0b00010000)? colorOn : colorOff);
// Segment F
- DrawDisplaySegment((Vector2){ position.x + segmentThick/2.0f, position.y + 2*segmentThick + segmentLen/2.0f - offsetYAdjust },
+ DrawDisplaySegment((Vector2){ position.x + segmentThick/2.0f, position.y + 2*segmentThick + segmentLen/2.0f - offsetYAdjust },
segmentLen, segmentThick, true, (segments & 0b00100000)? colorOn : colorOff);
// Segment G
- DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen/2.0f, position.y + 3*segmentThick + segmentLen - 2*offsetYAdjust },
+ DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen/2.0f, position.y + 3*segmentThick + segmentLen - 2*offsetYAdjust },
segmentLen, segmentThick, false, (segments & 0b01000000)? colorOn : colorOff);
}
@@ -284,11 +284,13 @@ static void DrawDisplaySegment(Vector2 center, int length, int thick, bool verti
if (!vertical)
{
// Horizontal segment points
- // 3___________________________5
- // / \
- // /1 x 6\
- // \ /
- // \2___________________________4/
+ /*
+ 3___________________________5
+ / \
+ /1 x 6\
+ \ /
+ \2___________________________4/
+ */
Vector2 segmentPointsH[6] = {
(Vector2){ center.x - length/2.0f - thick/2.0f, center.y }, // Point 1
(Vector2){ center.x - length/2.0f, center.y + thick/2.0f }, // Point 2
diff --git a/examples/shapes/shapes_double_pendulum.c b/examples/shapes/shapes_double_pendulum.c
index 4bcac02f9..760d66203 100644
--- a/examples/shapes/shapes_double_pendulum.c
+++ b/examples/shapes/shapes_double_pendulum.c
@@ -42,7 +42,7 @@ int main(void)
SetConfigFlags(FLAG_WINDOW_HIGHDPI);
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - double pendulum");
- // Simulation Paramters
+ // Simulation Parameters
float l1 = 15.0f, m1 = 0.2f, theta1 = DEG2RAD*170, w1 = 0;
float l2 = 15.0f, m2 = 0.1f, theta2 = DEG2RAD*0, w2 = 0;
float lengthScaler = 0.1f;
@@ -76,7 +76,7 @@ int main(void)
float step = dt/SIMULATION_STEPS, step2 = step*step;
// Update Physics - larger steps = better approximation
- for (int i = 0; i < SIMULATION_STEPS; ++i)
+ for (int i = 0; i < SIMULATION_STEPS; i++)
{
float delta = theta1 - theta2;
float sinD = sinf(delta), cosD = cosf(delta), cos2D = cosf(2*delta);
diff --git a/examples/shapes/shapes_kaleidoscope.c b/examples/shapes/shapes_kaleidoscope.c
index 07c96344c..119fca598 100644
--- a/examples/shapes/shapes_kaleidoscope.c
+++ b/examples/shapes/shapes_kaleidoscope.c
@@ -11,13 +11,29 @@
* 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 Hugo ARNAL (@hugoarnal)
+* Copyright (c) 2025 Hugo ARNAL (@hugoarnal) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
#include "raylib.h"
+#include
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"
#include "raymath.h"
+#define MAX_DRAW_LINES 8192
+
+// Line data type
+typedef struct {
+ Vector2 start;
+ Vector2 end;
+} Line;
+
+// Lines array as a global static variable to be stored
+// in heap and avoid potential stack overflow (on Web platform)
+static Line lines[MAX_DRAW_LINES] = { 0 };
+
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
@@ -30,22 +46,31 @@ int main(void)
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - kaleidoscope");
+ // Line drawing properties
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 };
+ Vector2 mousePos = { 0 };
Vector2 prevMousePos = { 0 };
-
- SetTargetFPS(60);
- ClearBackground(BLACK);
-
+ Vector2 scaleVector = { 1.0f, -1.0f };
Vector2 offset = { (float)screenWidth/2.0f, (float)screenHeight/2.0f };
+
Camera2D camera = { 0 };
camera.target = (Vector2){ 0 };
camera.offset = offset;
camera.rotation = 0.0f;
camera.zoom = 1.0f;
- Vector2 scaleVector = { 1.0f, -1.0f };
+ int currentLineCounter = 0;
+ int totalLineCounter = 0;
+ int resetButtonClicked = false;
+ int backButtonClicked = false;
+ int nextButtonClicked = false;
+
+ SetTargetFPS(20);
//--------------------------------------------------------------------------------------
// Main game loop
@@ -53,38 +78,93 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
- Vector2 mousePos = GetMousePosition();
+ prevMousePos = mousePos;
+ mousePos = GetMousePosition();
+
Vector2 lineStart = Vector2Subtract(mousePos, offset);
Vector2 lineEnd = Vector2Subtract(prevMousePos, offset);
+
+ if (
+ IsMouseButtonDown(MOUSE_LEFT_BUTTON)
+ && (CheckCollisionPointRec(mousePos, resetButtonRec) == false)
+ && (CheckCollisionPointRec(mousePos, backButtonRec) == false)
+ && (CheckCollisionPointRec(mousePos, nextButtonRec) == false)
+ )
+ {
+ for (int s = 0; (s < symmetry) && (totalLineCounter < (MAX_DRAW_LINES - 1)); s++)
+ {
+ lineStart = Vector2Rotate(lineStart, angle*DEG2RAD);
+ lineEnd = Vector2Rotate(lineEnd, angle*DEG2RAD);
+
+ // Store mouse line
+ lines[totalLineCounter].start = lineStart;
+ lines[totalLineCounter].end = lineEnd;
+
+ // Store reflective line
+ lines[totalLineCounter + 1].start = Vector2Multiply(lineStart, scaleVector);
+ lines[totalLineCounter + 1].end = Vector2Multiply(lineEnd, scaleVector);
+
+ totalLineCounter += 2;
+ currentLineCounter = totalLineCounter;
+ }
+ }
+
+ if (resetButtonClicked)
+ {
+ memset(&lines, 0, sizeof(Line)*MAX_DRAW_LINES);
+ currentLineCounter = 0;
+ totalLineCounter = 0;
+ }
+
+ if (backButtonClicked && (currentLineCounter > 0))
+ {
+ currentLineCounter -= 1;
+ }
+
+ if (nextButtonClicked && (currentLineCounter < MAX_DRAW_LINES) && ((currentLineCounter + 1) <= totalLineCounter))
+ {
+ currentLineCounter += 1;
+ }
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
+
+ ClearBackground(RAYWHITE);
BeginMode2D(camera);
- if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) {
- for (int i = 0; i < symmetry; i++) {
- lineStart = Vector2Rotate(lineStart, angle*DEG2RAD);
- lineEnd = Vector2Rotate(lineEnd, angle*DEG2RAD);
- DrawLineEx(lineStart, lineEnd, thickness, WHITE);
-
- Vector2 reflectLineStart = Vector2Multiply(lineStart, scaleVector);
- Vector2 reflectLineEnd = Vector2Multiply(lineEnd, scaleVector);
-
- DrawLineEx(reflectLineStart, reflectLineEnd, thickness, WHITE);
+ for (int s = 0; s < symmetry; s++)
+ {
+ for (int i = 0; i < currentLineCounter; i += 2)
+ {
+ DrawLineEx(lines[i].start, lines[i].end, thickness, BLACK);
+ DrawLineEx(lines[i + 1].start, lines[i + 1].end, thickness, BLACK);
}
}
- prevMousePos = mousePos;
EndMode2D();
+
+ if ((currentLineCounter - 1) < 0) GuiDisable();
+
+ backButtonClicked = GuiButton(backButtonRec, "<");
+ GuiEnable();
+
+ if ((currentLineCounter + 1) > totalLineCounter) GuiDisable();
+
+ nextButtonClicked = GuiButton(nextButtonRec, ">");
+ GuiEnable();
+ resetButtonClicked = GuiButton(resetButtonRec, "Reset");
+
+ DrawText(TextFormat("LINES: %i/%i", currentLineCounter, MAX_DRAW_LINES), 10, screenHeight - 30, 20, MAROON);
+ DrawFPS(10, 10);
+
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
-
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
diff --git a/examples/shapes/shapes_kaleidoscope.png b/examples/shapes/shapes_kaleidoscope.png
index d14571ab7..3c15a52b3 100644
Binary files a/examples/shapes/shapes_kaleidoscope.png and b/examples/shapes/shapes_kaleidoscope.png differ
diff --git a/examples/shapes/shapes_lines_drawing.c b/examples/shapes/shapes_lines_drawing.c
index aeaae3d45..76e81abb5 100644
--- a/examples/shapes/shapes_lines_drawing.c
+++ b/examples/shapes/shapes_lines_drawing.c
@@ -16,6 +16,7 @@
********************************************************************************************/
#include "raylib.h"
+
#include "raymath.h"
//------------------------------------------------------------------------------------
@@ -23,124 +24,114 @@
//------------------------------------------------------------------------------------
int main(void)
{
- // Initialization
- //--------------------------------------------------------------------------------------
- const int screenWidth = 800;
- const int screenHeight = 450;
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
- InitWindow(screenWidth, screenHeight, "raylib [shapes] example - lines drawing");
+ InitWindow(screenWidth, screenHeight, "raylib [shapes] example - lines drawing");
- // Hint text that shows before you click the screen
- bool startText = true;
+ // Hint text that shows before you click the screen
+ bool startText = true;
- // The mouse's position on the previous frame
- Vector2 mousePositionPrevious = GetMousePosition();
+ // The mouse's position on the previous frame
+ Vector2 mousePositionPrevious = GetMousePosition();
- // The canvas to draw lines on
- RenderTexture canvas = LoadRenderTexture(screenWidth, screenHeight);
+ // The canvas to draw lines on
+ RenderTexture canvas = LoadRenderTexture(screenWidth, screenHeight);
- // The background color of the canvas
- const Color backgroundColor = RAYWHITE;
+ // The line's thickness
+ float lineThickness = 8.0f;
+ // The lines hue (in HSV, from 0-360)
+ float lineHue = 0.0f;
- // The line's thickness
- float lineThickness = 8.0f;
- // The lines hue (in HSV, from 0-360)
- float lineHue = 0.0f;
+ // Clear the canvas to the background color
+ BeginTextureMode(canvas);
+ ClearBackground(RAYWHITE);
+ EndTextureMode();
- // Clear the canvas to the background color
- BeginTextureMode(canvas);
- ClearBackground(backgroundColor);
- EndTextureMode();
- //--------------------------------------------------------------------------------------
+ SetTargetFPS(60);
+ //--------------------------------------------------------------------------------------
- // Main game loop
- while (!WindowShouldClose()) // Detect window close button or ESC key
- {
- // Update
- //----------------------------------------------------------------------------------
- // Disable the hint text once the user clicks
- if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && startText)
- {
- startText = false;
- }
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ // Disable the hint text once the user clicks
+ if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && startText) startText = false;
- // Clear the canvas when the user middle-clicks
- if (IsMouseButtonPressed(MOUSE_BUTTON_MIDDLE))
- {
- BeginTextureMode(canvas);
- ClearBackground(backgroundColor);
- EndTextureMode();
- }
+ // Clear the canvas when the user middle-clicks
+ if (IsMouseButtonPressed(MOUSE_BUTTON_MIDDLE))
+ {
+ BeginTextureMode(canvas);
+ ClearBackground(RAYWHITE);
+ EndTextureMode();
+ }
- // Store whether the left and right buttons are down
- bool leftButtonDown = IsMouseButtonDown(MOUSE_BUTTON_LEFT);
- bool rightButtonDown = IsMouseButtonDown(MOUSE_BUTTON_RIGHT);
+ // Store whether the left and right buttons are down
+ bool leftButtonDown = IsMouseButtonDown(MOUSE_BUTTON_LEFT);
+ bool rightButtonDown = IsMouseButtonDown(MOUSE_BUTTON_RIGHT);
- if (leftButtonDown || rightButtonDown)
- {
- // The color for the line
- Color drawColor;
+ if (leftButtonDown || rightButtonDown)
+ {
+ // The color for the line
+ Color drawColor = WHITE;
- if (leftButtonDown)
- {
- // Increase the hue value by the distance our cursor has moved since the last frame (divided by 3)
- lineHue += Vector2Distance(mousePositionPrevious, GetMousePosition())/3.0f;
+ if (leftButtonDown)
+ {
+ // Increase the hue value by the distance our cursor has moved since the last frame (divided by 3)
+ lineHue += Vector2Distance(mousePositionPrevious, GetMousePosition())/3.0f;
- // While the hue is >=360, subtract it to bring it down into the range 0-360
- // This is more visually accurate than resetting to zero
- while (lineHue >= 360.0f)
- {
- lineHue -= 360.0f;
- }
+ // While the hue is >=360, subtract it to bring it down into the range 0-360
+ // This is more visually accurate than resetting to zero
+ while (lineHue >= 360.0f) lineHue -= 360.0f;
- // Create the final color
- drawColor = ColorFromHSV(lineHue, 1.0f, 1.0f);
- }
- else if (rightButtonDown)
- {
- // Use the background color as an "eraser"
- drawColor = backgroundColor;
- }
+ // Create the final color
+ drawColor = ColorFromHSV(lineHue, 1.0f, 1.0f);
+ }
+ else if (rightButtonDown) drawColor = RAYWHITE; // Use the background color as an "eraser"
- // Draw the line onto the canvas
- BeginTextureMode(canvas);
- // Circles act as "caps", smoothing corners
- DrawCircleV(mousePositionPrevious, lineThickness/2.0f, drawColor);
- DrawCircleV(GetMousePosition(), lineThickness/2.0f, drawColor);
- DrawLineEx(mousePositionPrevious, GetMousePosition(), lineThickness, drawColor);
- EndTextureMode();
- }
+ // Draw the line onto the canvas
+ BeginTextureMode(canvas);
+ // Circles act as "caps", smoothing corners
+ DrawCircleV(mousePositionPrevious, lineThickness/2.0f, drawColor);
+ DrawCircleV(GetMousePosition(), lineThickness/2.0f, drawColor);
+ DrawLineEx(mousePositionPrevious, GetMousePosition(), lineThickness, drawColor);
+ EndTextureMode();
+ }
- // Update line thickness based on mousewheel
- lineThickness += GetMouseWheelMove();
- lineThickness = Clamp(lineThickness, 1.0, 500.0f);
+ // Update line thickness based on mousewheel
+ lineThickness += GetMouseWheelMove();
+ lineThickness = Clamp(lineThickness, 1.0, 500.0f);
- // Update mouse's previous position
- mousePositionPrevious = GetMousePosition();
- //----------------------------------------------------------------------------------
+ // Update mouse's previous position
+ mousePositionPrevious = GetMousePosition();
+ //----------------------------------------------------------------------------------
- // Draw
- //----------------------------------------------------------------------------------
- BeginDrawing();
- // Draw the render texture to the screen, flipped vertically to make it appear top-side up
- DrawTextureRec(canvas.texture, (Rectangle){ 0.0f, 0.0f, (float)canvas.texture.width,(float)-canvas.texture.height }, Vector2Zero(), WHITE);
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
- // Draw the preview circle
- if (!leftButtonDown) DrawCircleLinesV(GetMousePosition(), lineThickness/2.0f, (Color){ 127, 127, 127, 127 });
+ // Draw the render texture to the screen, flipped vertically to make it appear top-side up
+ DrawTextureRec(canvas.texture, (Rectangle){ 0.0f, 0.0f, (float)canvas.texture.width,(float)-canvas.texture.height }, Vector2Zero(), WHITE);
- // Draw the hint text
- if (startText) DrawText("try clicking and dragging!", 275, 215, 20, LIGHTGRAY);
- EndDrawing();
- //----------------------------------------------------------------------------------
- }
+ // Draw the preview circle
+ if (!leftButtonDown) DrawCircleLinesV(GetMousePosition(), lineThickness/2.0f, (Color){ 127, 127, 127, 127 });
- // De-Initialization
- //--------------------------------------------------------------------------------------
- // Unload the canvas render texture
- UnloadRenderTexture(canvas);
+ // Draw the hint text
+ if (startText) DrawText("try clicking and dragging!", 275, 215, 20, LIGHTGRAY);
- CloseWindow(); // Close window and OpenGL context
- //--------------------------------------------------------------------------------------
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
- return 0;
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ UnloadRenderTexture(canvas); // Unload the canvas render texture
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
}
\ No newline at end of file
diff --git a/examples/shapes/shapes_math_angle_rotation.c b/examples/shapes/shapes_math_angle_rotation.c
index f895026f5..63c9aaa91 100644
--- a/examples/shapes/shapes_math_angle_rotation.c
+++ b/examples/shapes/shapes_math_angle_rotation.c
@@ -31,7 +31,7 @@ int main(void)
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - math angle rotation");
SetTargetFPS(60);
- Vector2 center = { screenWidth / 2.0f, screenHeight / 2.0f };
+ Vector2 center = { screenWidth/2.0f, screenHeight/2.0f };
const float lineLength = 150.0f;
// Predefined angles for fixed lines
@@ -60,9 +60,9 @@ int main(void)
// Draw fixed-angle lines with colorful gradient
for (int i = 0; i < numAngles; i++)
{
- float rad = angles[i] * DEG2RAD;
- Vector2 end = { center.x + cosf(rad) * lineLength,
- center.y + sinf(rad) * lineLength };
+ float rad = angles[i]*DEG2RAD;
+ Vector2 end = { center.x + cosf(rad)*lineLength,
+ center.y + sinf(rad)*lineLength };
// Gradient color from green → cyan → blue → magenta
Color col;
@@ -78,15 +78,15 @@ int main(void)
DrawLineEx(center, end, 5.0f, col);
// Draw angle label slightly offset along the line
- Vector2 textPos = { center.x + cosf(rad) * (lineLength + 20),
- center.y + sinf(rad) * (lineLength + 20) };
+ Vector2 textPos = { center.x + cosf(rad)*(lineLength + 20),
+ center.y + sinf(rad)*(lineLength + 20) };
DrawText(TextFormat("%d°", angles[i]), (int)textPos.x, (int)textPos.y, 20, col);
}
// Draw animated rotating line with changing color
- float animRad = totalAngle * DEG2RAD;
- Vector2 animEnd = { center.x + cosf(animRad) * lineLength,
- center.y + sinf(animRad) * lineLength };
+ float animRad = totalAngle*DEG2RAD;
+ Vector2 animEnd = { center.x + cosf(animRad)*lineLength,
+ center.y + sinf(animRad)*lineLength };
// Cycle through HSV colors for animated line
Color animCol = ColorFromHSV(fmodf(totalAngle, 360.0f), 0.8f, 0.9f);
diff --git a/examples/shapes/shapes_math_sine_cosine.c b/examples/shapes/shapes_math_sine_cosine.c
index d8d13920e..c3646cabd 100644
--- a/examples/shapes/shapes_math_sine_cosine.c
+++ b/examples/shapes/shapes_math_sine_cosine.c
@@ -35,13 +35,13 @@ int main(void)
const int screenWidth = 800;
const int screenHeight = 450;
- SetConfigFlags(FLAG_MSAA_4X_HINT);
+ SetConfigFlags(FLAG_MSAA_4X_HINT);
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - math sine cosine");
Vector2 sinePoints[WAVE_POINTS];
Vector2 cosPoints[WAVE_POINTS];
- Vector2 center = { (screenWidth/2.0f) - 30.f, screenHeight/2.0f };
- Rectangle start = { 20.f, screenHeight - 120.f , 200.0f, 100.0f};
+ Vector2 center = { (screenWidth/2.0f) - 30.0f, screenHeight/2.0f };
+ Rectangle start = { 20.0f, screenHeight - 120.f , 200.0f, 100.0f};
float radius = 130.0f;
float angle = 0.0f;
bool pause = false;
@@ -97,8 +97,8 @@ int main(void)
// Base circle and axes
DrawCircleLinesV(center, radius, GRAY);
- DrawLineEx((Vector2){ center.x, limitMin.y }, (Vector2){ center.x, limitMax.y }, 1.0f, GRAY);
- DrawLineEx((Vector2){ limitMin.x, center.y }, (Vector2){ limitMax.x, center.y }, 1.f, GRAY);
+ DrawLineEx((Vector2){ center.x, limitMin.y }, (Vector2){ center.x, limitMax.y }, 1.0f, GRAY);
+ DrawLineEx((Vector2){ limitMin.x, center.y }, (Vector2){ limitMax.x, center.y }, 1.0f, GRAY);
// Wave graph axes
DrawLineEx((Vector2){ start.x , start.y }, (Vector2){ start.x , start.y + start.height }, 2.0f, GRAY);
@@ -110,17 +110,17 @@ int main(void)
DrawText("0", start.x - 8, start.y + start.height/2 - 6, 6, GRAY);
DrawText("-1", start.x - 12, start.y + start.height - 8, 6, GRAY);
DrawText("0", start.x - 2, start.y + start.height + 4, 6, GRAY);
- DrawText("360", start.x + start.width - 8, start.y + start.height + 4, 6, GRAY);
+ DrawText("360", start.x + start.width - 8, start.y + start.height + 4, 6, GRAY);
// Sine (red - vertical)
- DrawLineEx((Vector2){ center.x, center.y }, (Vector2){ center.x, point.y }, 2.0f, RED);
+ DrawLineEx((Vector2){ center.x, center.y }, (Vector2){ center.x, point.y }, 2.0f, RED);
DrawLineDashed((Vector2){ point.x, center.y }, (Vector2){ point.x, point.y }, 10.0f, 4.0f, RED);
DrawText(TextFormat("Sine %.2f", sinRad), 640, 190, 6, RED);
DrawCircleV((Vector2){ start.x + (angle/360.0f)*start.width, start.y + ((-sinRad + 1)*start.height/2.0f) }, 4.0f, RED);
DrawSplineLinear(sinePoints, WAVE_POINTS, 1.0f, RED);
// Cosine (blue - horizontal)
- DrawLineEx((Vector2){ center.x, center.y }, (Vector2){ point.x, center.y }, 2.0f, BLUE);
+ DrawLineEx((Vector2){ center.x, center.y }, (Vector2){ point.x, center.y }, 2.0f, BLUE);
DrawLineDashed((Vector2){ center.x , point.y }, (Vector2){ point.x, point.y }, 10.0f, 4.0f, BLUE);
DrawText(TextFormat("Cosine %.2f", cosRad), 640, 210, 6, BLUE);
DrawCircleV((Vector2){ start.x + (angle/360.0f)*start.width, start.y + ((-cosRad + 1)*start.height/2.0f) }, 4.0f, BLUE);
@@ -135,32 +135,33 @@ int main(void)
DrawText(TextFormat("Cotangent %.2f", cotangent), 640, 250, 6, ORANGE);
// Complementary angle (beige)
- DrawCircleSectorLines(center, radius*0.6f , -angle, -90.f , 36.0f, BEIGE);
+ DrawCircleSectorLines(center, radius*0.6f , -angle, -90.0f , 36.0f, BEIGE);
DrawText(TextFormat("Complementary %0.f°",complementary), 640, 150, 6, BEIGE);
// Supplementary angle (darkblue)
- DrawCircleSectorLines(center, radius*0.5f , -angle, -180.f , 36.0f, DARKBLUE);
+ DrawCircleSectorLines(center, radius*0.5f , -angle, -180.0f , 36.0f, DARKBLUE);
DrawText(TextFormat("Supplementary %0.f°",supplementary), 640, 130, 6, DARKBLUE);
// Explementary angle (pink)
- DrawCircleSectorLines(center, radius*0.4f , -angle, -360.f , 36.0f, PINK);
+ DrawCircleSectorLines(center, radius*0.4f , -angle, -360.0f , 36.0f, PINK);
DrawText(TextFormat("Explementary %0.f°",explementary), 640, 170, 6, PINK);
// Current angle - arc (lime), radius (black), endpoint (black)
- DrawCircleSectorLines(center, radius*0.7f , -angle, 0.f, 36.0f, LIME);
+ DrawCircleSectorLines(center, radius*0.7f , -angle, 0.0f, 36.0f, LIME);
DrawLineEx((Vector2){ center.x , center.y }, point, 2.0f, BLACK);
DrawCircleV(point, 4.0f, BLACK);
// Draw GUI controls
//------------------------------------------------------------------------------
GuiSetStyle(LABEL, TEXT_COLOR_NORMAL, ColorToInt(GRAY));
- GuiToggle((Rectangle){ 640, 70, 120, 20}, TextFormat("Pause"), &pause);
+ GuiToggle((Rectangle){ 640, 70, 120, 20}, TextFormat("Pause"), &pause);
GuiSetStyle(LABEL, TEXT_COLOR_NORMAL, ColorToInt(LIME));
- GuiSliderBar((Rectangle){ 640, 40, 120, 20}, "Angle", TextFormat("%.0f°", angle), &angle, 0.0f, 360.f);
+ GuiSliderBar((Rectangle){ 640, 40, 120, 20}, "Angle", TextFormat("%.0f°", angle), &angle, 0.0f, 360.0f);
// Angle values panel
- GuiGroupBox((Rectangle){ 620, 110, 140, 170}, "Angle Values");
+ GuiGroupBox((Rectangle){ 620, 110, 140, 170}, "Angle Values");
//------------------------------------------------------------------------------
+
DrawFPS(10, 10);
EndDrawing();
diff --git a/examples/shapes/shapes_mouse_trail.c b/examples/shapes/shapes_mouse_trail.c
index e0e5a3c1d..3dd0fbd7f 100644
--- a/examples/shapes/shapes_mouse_trail.c
+++ b/examples/shapes/shapes_mouse_trail.c
@@ -35,7 +35,7 @@ int main(void)
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - mouse trail");
// Array to store the history of mouse positions (our fixed-size queue)
- Vector2 trailPositions[MAX_TRAIL_LENGTH] = { 0 };
+ Vector2 trailPositions[MAX_TRAIL_LENGTH] = { 0 };
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
@@ -62,8 +62,8 @@ int main(void)
//----------------------------------------------------------------------------------
BeginDrawing();
- ClearBackground(BLACK);
-
+ ClearBackground(BLACK);
+
// Draw the trail by looping through the history array
for (int i = 0; i < MAX_TRAIL_LENGTH; i++)
{
@@ -71,22 +71,22 @@ int main(void)
if ((trailPositions[i].x != 0.0f) || (trailPositions[i].y != 0.0f))
{
// Calculate relative trail strength (ratio is near 1.0 for new, near 0.0 for old)
- float ratio = (float)(MAX_TRAIL_LENGTH - i) / MAX_TRAIL_LENGTH;
-
+ float ratio = (float)(MAX_TRAIL_LENGTH - i)/MAX_TRAIL_LENGTH;
+
// Fade effect: oldest positions are more transparent
// Fade (color, alpha) - alpha is 0.5 to 1.0 based on ratio
- Color trailColor = Fade(SKYBLUE, ratio*0.5f + 0.5f);
-
+ Color trailColor = Fade(SKYBLUE, ratio*0.5f + 0.5f);
+
// Size effect: oldest positions are smaller
- float trailRadius = 15.0f*ratio;
-
+ float trailRadius = 15.0f*ratio;
+
DrawCircleV(trailPositions[i], trailRadius, trailColor);
}
}
// Draw a distinct white circle for the current mouse position (Index 0)
DrawCircleV(mousePosition, 15.0f, WHITE);
-
+
DrawText("Move the mouse to see the trail effect!", 10, screenHeight - 30, 20, LIGHTGRAY);
EndDrawing();
diff --git a/examples/shapes/shapes_penrose_tile.c b/examples/shapes/shapes_penrose_tile.c
new file mode 100644
index 000000000..304dca3cc
--- /dev/null
+++ b/examples/shapes/shapes_penrose_tile.c
@@ -0,0 +1,271 @@
+/*******************************************************************************************
+*
+* raylib [shapes] example - penrose tile
+*
+* Example complexity rating: [★★★★] 4/4
+*
+* Example originally created with raylib 5.5, last time updated with raylib 5.6-dev
+* Based on: https://processing.org/examples/penrosetile.html
+*
+* Example contributed by David Buzatto (@davidbuzatto) 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 David Buzatto (@davidbuzatto)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include
+#include
+#include
+
+#define STR_MAX_SIZE 10000
+#define TURTLE_STACK_MAX_SIZE 50
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef struct TurtleState {
+ Vector2 origin;
+ double angle;
+} TurtleState;
+
+typedef struct PenroseLSystem {
+ int steps;
+ char *production;
+ const char *ruleW;
+ const char *ruleX;
+ const char *ruleY;
+ const char *ruleZ;
+ float drawLength;
+ float theta;
+} PenroseLSystem;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static TurtleState turtleStack[TURTLE_STACK_MAX_SIZE];
+static int turtleTop = -1;
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+static void PushTurtleState(TurtleState state);
+static TurtleState PopTurtleState(void);
+static PenroseLSystem CreatePenroseLSystem(float drawLength);
+static void BuildProductionStep(PenroseLSystem *ls);
+static void DrawPenroseLSystem(PenroseLSystem *ls);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ SetConfigFlags(FLAG_MSAA_4X_HINT);
+ InitWindow(screenWidth, screenHeight, "raylib [shapes] example - penrose tile");
+
+ float drawLength = 460.0f;
+ int minGenerations = 0;
+ int maxGenerations = 4;
+ int generations = 0;
+
+ // Initializee new penrose tile
+ PenroseLSystem ls = CreatePenroseLSystem(drawLength*(generations/(float)maxGenerations));
+ for (int i = 0; i < generations; i++) BuildProductionStep(&ls);
+
+ SetTargetFPS(120); // Set our game to run at 120 frames-per-second
+ //---------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ bool rebuild = false;
+ if (IsKeyPressed(KEY_UP))
+ {
+ if (generations < maxGenerations)
+ {
+ generations++;
+ rebuild = true;
+ }
+ }
+ else if (IsKeyPressed(KEY_DOWN))
+ {
+ if (generations > minGenerations)
+ {
+ generations--;
+ if (generations > 0) rebuild = true;
+ }
+ }
+
+ if (rebuild)
+ {
+ RL_FREE(ls.production); // Free previous production for re-creation
+ ls = CreatePenroseLSystem(drawLength*(generations/(float)maxGenerations));
+ for (int i = 0; i < generations; i++) BuildProductionStep(&ls);
+ }
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground( RAYWHITE );
+
+ if (generations > 0) DrawPenroseLSystem(&ls);
+
+ DrawText("penrose l-system", 10, 10, 20, DARKGRAY);
+ DrawText("press up or down to change generations", 10, 30, 20, DARKGRAY);
+ DrawText(TextFormat("generations: %d", generations), 10, 50, 20, DARKGRAY);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+// Push turtle state for next step
+static void PushTurtleState(TurtleState state)
+{
+ if (turtleTop < (TURTLE_STACK_MAX_SIZE - 1)) turtleStack[++turtleTop] = state;
+ else TraceLog(LOG_WARNING, "TURTLE STACK OVERFLOW!");
+}
+
+// Pop turtle state step
+static TurtleState PopTurtleState(void)
+{
+ if (turtleTop >= 0) return turtleStack[turtleTop--];
+ else TraceLog(LOG_WARNING, "TURTLE STACK UNDERFLOW!");
+
+ return (TurtleState){ 0 };
+}
+
+// Create a new penrose tile structure
+static PenroseLSystem CreatePenroseLSystem(float drawLength)
+{
+ // TODO: Review constant values assignment on recreation?
+ PenroseLSystem ls = {
+ .steps = 0,
+ .ruleW = "YF++ZF4-XF[-YF4-WF]++",
+ .ruleX = "+YF--ZF[3-WF--XF]+",
+ .ruleY = "-WF++XF[+++YF++ZF]-",
+ .ruleZ = "--YF++++WF[+ZF++++XF]--XF",
+ .drawLength = drawLength,
+ .theta = 36.0f // Degrees
+ };
+
+ ls.production = (char *)RL_MALLOC(sizeof(char)*STR_MAX_SIZE);
+ ls.production[0] = '\0';
+ strncpy(ls.production, "[X]++[X]++[X]++[X]++[X]", STR_MAX_SIZE);
+
+ return ls;
+}
+
+// Build next penrose step
+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);
+
+ for (int i = 0; i < productionLength; i++)
+ {
+ char step = ls->production[i];
+ int remainingSpace = STR_MAX_SIZE - strnlen(newProduction, STR_MAX_SIZE) - 1;
+ switch (step)
+ {
+ case 'W': strncat(newProduction, ls->ruleW, remainingSpace); break;
+ case 'X': strncat(newProduction, ls->ruleX, remainingSpace); break;
+ case 'Y': strncat(newProduction, ls->ruleY, remainingSpace); break;
+ case 'Z': strncat(newProduction, ls->ruleZ, remainingSpace); break;
+ default:
+ {
+ if (step != 'F')
+ {
+ int t = strnlen(newProduction, STR_MAX_SIZE);
+ newProduction[t] = step;
+ newProduction[t + 1] = '\0';
+ }
+ } break;
+ }
+ }
+
+ ls->drawLength *= 0.5f;
+ strncpy(ls->production, newProduction, STR_MAX_SIZE);
+
+ RL_FREE(newProduction);
+}
+
+// Draw penrose tile lines
+static void DrawPenroseLSystem(PenroseLSystem *ls)
+{
+ Vector2 screenCenter = { GetScreenWidth()/2, GetScreenHeight()/2 };
+
+ TurtleState turtle = {
+ .origin = { 0 },
+ .angle = -90.0f
+ };
+
+ int repeats = 1;
+ int productionLength = (int)strnlen(ls->production, STR_MAX_SIZE);
+ ls->steps += 12;
+
+ if (ls->steps > productionLength) ls->steps = productionLength;
+
+ for (int i = 0; i < ls->steps; i++)
+ {
+ char step = ls->production[i];
+ if (step == 'F')
+ {
+ for (int j = 0; j < repeats; j++)
+ {
+ Vector2 startPosWorld = turtle.origin;
+ float radAngle = DEG2RAD*turtle.angle;
+ turtle.origin.x += ls->drawLength*cosf(radAngle);
+ turtle.origin.y += ls->drawLength*sinf(radAngle);
+ 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));
+ }
+
+ repeats = 1;
+ }
+ else if (step == '+')
+ {
+ for (int j = 0; j < repeats; j++) turtle.angle += ls->theta;
+
+ repeats = 1;
+ }
+ else if (step == '-')
+ {
+ for (int j = 0; j < repeats; j++) turtle.angle += -ls->theta;
+
+ repeats = 1;
+ }
+ else if (step == '[') PushTurtleState(turtle);
+ else if (step == ']') turtle = PopTurtleState();
+ else if ((step >= 48) && (step <= 57)) repeats = (int) step - 48;
+ }
+
+ turtleTop = -1;
+}
diff --git a/examples/shapes/shapes_penrose_tile.png b/examples/shapes/shapes_penrose_tile.png
new file mode 100644
index 000000000..dffc35cc4
Binary files /dev/null and b/examples/shapes/shapes_penrose_tile.png differ
diff --git a/examples/shapes/shapes_pie_chart.c b/examples/shapes/shapes_pie_chart.c
index 566e3a4d1..6db7f96da 100644
--- a/examples/shapes/shapes_pie_chart.c
+++ b/examples/shapes/shapes_pie_chart.c
@@ -67,7 +67,7 @@ int main(void)
const Rectangle panelRect = {
panelPos.x, panelPos.y,
(float)panelWidth,
- (float)screenHeight - 2.0f*panelMargin
+ (float)screenHeight - 2.0f*panelMargin
};
// Pie chart geometry
@@ -108,13 +108,13 @@ int main(void)
for (int i = 0; i < sliceCount; i++)
{
float sweep = (totalValue > 0)? (values[i]/totalValue)*360.0f : 0.0f;
-
+
if ((angle >= currentAngle) && (angle < (currentAngle + sweep)))
{
hoveredSlice = i;
break;
}
-
+
currentAngle += sweep;
}
}
@@ -182,11 +182,11 @@ int main(void)
GuiLine((Rectangle){ panelPos.x + 10, (float)panelPos.y + 12 + 170, panelRect.width - 20, 1 }, NULL);
// Scrollable area for slice editors
- scrollPanelBounds = (Rectangle){
- panelPos.x + panelMargin,
- (float)panelPos.y + 12 + 190,
- panelRect.width - panelMargin*2,
- panelRect.y + panelRect.height - panelPos.y + 12 + 190 - panelMargin
+ scrollPanelBounds = (Rectangle){
+ panelPos.x + panelMargin,
+ (float)panelPos.y + 12 + 190,
+ panelRect.width - panelMargin*2,
+ panelRect.y + panelRect.height - panelPos.y + 12 + 190 - panelMargin
};
int contentHeight = sliceCount*35;
diff --git a/examples/shapes/shapes_rectangle_advanced.c b/examples/shapes/shapes_rectangle_advanced.c
index 15487ee55..c274cb6f8 100644
--- a/examples/shapes/shapes_rectangle_advanced.c
+++ b/examples/shapes/shapes_rectangle_advanced.c
@@ -184,7 +184,7 @@ static void DrawRectangleRoundedGradientH(Rectangle rec, float roundnessLeft, fl
}
// End one even segments
- if ( segments % 2)
+ if ( segments%2)
{
rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
rlVertex2f(center.x, center.y);
diff --git a/examples/shapes/shapes_recursive_tree.c b/examples/shapes/shapes_recursive_tree.c
index e9cadd8ec..f15758773 100644
--- a/examples/shapes/shapes_recursive_tree.c
+++ b/examples/shapes/shapes_recursive_tree.c
@@ -45,7 +45,7 @@ int main(void)
Vector2 start = { (screenWidth/2.0f) - 125.0f, (float)screenHeight };
float angle = 40.0f;
- float thick = 1.0f;
+ float thick = 1.0f;
float treeDepth = 10.0f;
float branchDecay = 0.66f;
float length = 120.0f;
@@ -67,21 +67,21 @@ int main(void)
Vector2 initialEnd = { start.x + length*sinf(0.0f), start.y - length*cosf(0.0f) };
branches[count++] = (Branch){start, initialEnd, 0.0f, length};
- for (int i = 0; i < count; i++)
+ for (int i = 0; i < count; i++)
{
Branch branch = branches[i];
if (branch.length < 2) continue;
float nextLength = branch.length*branchDecay;
- if (count < maxBranches && nextLength >= 2)
+ if (count < maxBranches && nextLength >= 2)
{
Vector2 branchStart = branch.end;
float angle1 = branch.angle + theta;
Vector2 branchEnd1 = { branchStart.x + nextLength*sinf(angle1), branchStart.y - nextLength*cosf(angle1) };
branches[count++] = (Branch){branchStart, branchEnd1, angle1, nextLength};
-
+
float angle2 = branch.angle - theta;
Vector2 branchEnd2 = { branchStart.x + nextLength*sinf(angle2), branchStart.y - nextLength*cosf(angle2) };
branches[count++] = (Branch){branchStart, branchEnd2, angle2, nextLength};
@@ -94,10 +94,10 @@ int main(void)
ClearBackground(RAYWHITE);
- for (int i = 0; i < count; i++)
+ for (int i = 0; i < count; i++)
{
Branch branch = branches[i];
- if (branch.length >= 2)
+ if (branch.length >= 2)
{
if (bezier) DrawLineBezier(branch.start, branch.end, thick, RED);
else DrawLineEx(branch.start, branch.end, thick, RED);
@@ -112,7 +112,7 @@ int main(void)
GuiSliderBar((Rectangle){ 640, 40, 120, 20}, "Angle", TextFormat("%.0f", angle), &angle, 0, 180);
GuiSliderBar((Rectangle){ 640, 70, 120, 20 }, "Length", TextFormat("%.0f", length), &length, 12.0f, 240.0f);
GuiSliderBar((Rectangle){ 640, 100, 120, 20}, "Decay", TextFormat("%.2f", branchDecay), &branchDecay, 0.1f, 0.78f);
- GuiSliderBar((Rectangle){ 640, 130, 120, 20 }, "Depth", TextFormat("%.0f", treeDepth), &treeDepth, 1.0f, 10.f);
+ GuiSliderBar((Rectangle){ 640, 130, 120, 20 }, "Depth", TextFormat("%.0f", treeDepth), &treeDepth, 1.0f, 10.0f);
GuiSliderBar((Rectangle){ 640, 160, 120, 20}, "Thick", TextFormat("%.0f", thick), &thick, 1, 8);
GuiCheckBox((Rectangle){ 640, 190, 20, 20 }, "Bezier", &bezier);
//------------------------------------------------------------------------------
diff --git a/examples/shapes/shapes_rlgl_color_wheel.c b/examples/shapes/shapes_rlgl_color_wheel.c
new file mode 100644
index 000000000..f02226a83
--- /dev/null
+++ b/examples/shapes/shapes_rlgl_color_wheel.c
@@ -0,0 +1,274 @@
+/*******************************************************************************************
+*
+* raylib [shapes] example - rlgl color wheel
+*
+* Example complexity rating: [★★★☆] 3/4
+*
+* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+* Example contributed by Robin (@RobinsAviary) 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 Robin (@RobinsAviary)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include "rlgl.h"
+#include "raymath.h"
+#include
+#include
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ // The minimum/maximum points the circle can have
+ const unsigned int pointsMin = 3;
+ const unsigned int pointsMax = 256;
+
+ // The current number of points and the radius of the circle
+ unsigned int triangleCount = 64;
+ float pointScale = 150.0f;
+
+ // Slider value, literally maps to value in HSV
+ float value = 1.0f;
+
+ // The center of the screen
+ Vector2 center = { (float)screenWidth/2.0f, (float)screenHeight/2.0f };
+ // The location of the color wheel
+ Vector2 circlePosition = center;
+
+ // The currently selected color
+ Color color = { 255, 255, 255, 255 };
+
+ // Indicates if the slider is being clicked
+ bool sliderClicked = false;
+
+ // Indicates if the current color going to be updated, as well as the handle position
+ bool settingColor = false;
+
+ // How the color wheel will be rendered
+ unsigned int renderType = RL_TRIANGLES;
+
+ // Enable anti-aliasing
+ SetConfigFlags(FLAG_MSAA_4X_HINT);
+ InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rlgl color wheel");
+
+ SetTargetFPS(60);
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ triangleCount += (unsigned int)GetMouseWheelMove();
+ triangleCount = (unsigned int)Clamp((float)triangleCount, (float)pointsMin, (float)pointsMax);
+
+ Rectangle sliderRectangle = { 42.0f, 16.0f + 64.0f + 45.0f, 64.0f, 16.0f };
+ Vector2 mousePosition = GetMousePosition();
+
+ // Checks if the user is hovering over the value slider
+ bool sliderHover = (mousePosition.x >= sliderRectangle.x && mousePosition.y >= sliderRectangle.y && mousePosition.x < sliderRectangle.x + sliderRectangle.width && mousePosition.y < sliderRectangle.y + sliderRectangle.height);
+
+ // Copy color as hex
+ if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyDown(KEY_C))
+ {
+ if (IsKeyPressed(KEY_C))
+ {
+ SetClipboardText(TextFormat("#%02X%02X%02X", color.r, color.g, color.b));
+ }
+ }
+
+ // Scale up the color wheel, adjusting the handle visually
+ if (IsKeyDown(KEY_UP))
+ {
+ pointScale *= 1.025f;
+
+ if (pointScale > (float)screenHeight/2.0f)
+ {
+ pointScale = (float)screenHeight/2.0f;
+ }
+ else
+ {
+ circlePosition = Vector2Add(Vector2Multiply(Vector2Subtract(circlePosition, center), (Vector2){ 1.025f, 1.025f }), center);
+ }
+ }
+
+ // Scale down the wheel, adjusting the handle visually
+ if (IsKeyDown(KEY_DOWN))
+ {
+ pointScale *= 0.975f;
+
+ if (pointScale < 32.0f)
+ {
+ pointScale = 32.0f;
+ }
+ else
+ {
+ circlePosition = Vector2Add(Vector2Multiply(Vector2Subtract(circlePosition, center), (Vector2){ 0.975f, 0.975f }), center);
+ }
+
+ float distance = Vector2Distance(center, circlePosition)/pointScale;
+ float angle = ((Vector2Angle((Vector2){ 0.0f, -pointScale }, Vector2Subtract(center, circlePosition))/PI + 1.0f)/2.0f);
+
+ if (distance > 1.0f)
+ {
+ circlePosition = Vector2Add((Vector2){ sinf(angle*(PI*2.0f))*pointScale, -cosf(angle*(PI*2.0f))*pointScale }, center);
+ }
+ }
+
+ // Checks if the user clicked on the color wheel
+ if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && Vector2Distance(GetMousePosition(), center) <= pointScale + 10.0f)
+ {
+ settingColor = true;
+ }
+
+ // Update flag when mouse button is released
+ if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) settingColor = false;
+
+ // Check if the user clicked/released the slider for the color's value
+ if (sliderHover && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) sliderClicked = true;
+
+ if (sliderClicked && IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) sliderClicked = false;
+
+ // Update render mode accordingly
+ if (IsKeyPressed(KEY_SPACE)) renderType = RL_LINES;
+
+ if (IsKeyReleased(KEY_SPACE)) renderType = RL_TRIANGLES;
+
+ // If the slider or the wheel was clicked, update the current color
+ if (settingColor || sliderClicked)
+ {
+ if (settingColor) circlePosition = GetMousePosition();
+
+ float distance = Vector2Distance(center, circlePosition)/pointScale;
+
+ float angle = ((Vector2Angle((Vector2){ 0.0f, -pointScale }, Vector2Subtract(center, circlePosition))/PI + 1.0f)/2.0f);
+ if (settingColor && distance > 1.0f) circlePosition = Vector2Add((Vector2){ sinf(angle*(PI*2.0f))*pointScale, -cosf(angle*(PI* 2.0f))*pointScale }, center);
+
+ float angle360 = angle*360.0f;
+ float valueActual = Clamp(distance, 0.0f, 1.0f);
+ color = ColorLerp((Color){ (int)(value*255.0f), (int)(value*255.0f), (int)(value*255.0f), 255 }, ColorFromHSV(angle360, Clamp(distance, 0.0f, 1.0f), 1.0f), valueActual);
+ }
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ // Begin rendering color wheel
+ rlBegin(renderType);
+ for (unsigned int i = 0; i < triangleCount; i++)
+ {
+ float angleOffset = ((PI*2.0f)/(float)triangleCount);
+ float angle = angleOffset*(float)i;
+ float angleOffsetCalculated = ((float)i + 1)*angleOffset;
+ Vector2 scale = (Vector2){ pointScale, pointScale };
+
+ Vector2 offset = Vector2Multiply((Vector2){ sinf(angle), -cosf(angle) }, scale);
+ Vector2 offset2 = Vector2Multiply((Vector2){ sinf(angleOffsetCalculated), -cosf(angleOffsetCalculated) }, scale);
+
+ Vector2 position = Vector2Add(center, offset);
+ Vector2 position2 = Vector2Add(center, offset2);
+
+ float angleNonRadian = (angle/(2.0f*PI))*360.0f;
+ float angleNonRadianOffset = (angleOffset/(2.0f*PI))*360.0f;
+
+ Color currentColor = ColorFromHSV(angleNonRadian, 1.0f, 1.0f);
+ Color offsetColor = ColorFromHSV(angleNonRadian + angleNonRadianOffset, 1.0f, 1.0f);
+
+ // Input vertices differently depending on mode
+ if (renderType == RL_TRIANGLES)
+ {
+ // RL_TRIANGLES expects three vertices per triangle
+ rlColor4ub(currentColor.r, currentColor.g, currentColor.b, currentColor.a);
+ rlVertex2f(position.x, position.y);
+ rlColor4f(value, value, value, 1.0f);
+ rlVertex2f(center.x, center.y);
+ rlColor4ub(offsetColor.r, offsetColor.g, offsetColor.b, offsetColor.a);
+ rlVertex2f(position2.x, position2.y);
+ }
+ else if (renderType == RL_LINES)
+ {
+ // RL_LINES expects two vertices per line
+ rlColor4ub(currentColor.r, currentColor.g, currentColor.b, currentColor.a);
+ rlVertex2f(position.x, position.y);
+ rlColor4ub(WHITE.r, WHITE.g, WHITE.b, WHITE.a);
+ rlVertex2f(center.x, center.y);
+
+ rlVertex2f(center.x, center.y);
+ rlColor4ub(offsetColor.r, offsetColor.g, offsetColor.b, offsetColor.a);
+ rlVertex2f(position2.x, position2.y);
+
+ rlVertex2f(position2.x, position2.y);
+ rlColor4ub(currentColor.r, currentColor.g, currentColor.b, currentColor.a);
+ rlVertex2f(position.x, position.y);
+ }
+ }
+ rlEnd();
+
+ // Make the handle slightly more visible overtop darker colors
+ Color handleColor = BLACK;
+
+ if (Vector2Distance(center, circlePosition)/pointScale <= 0.5f && value <= 0.5f)
+ {
+ handleColor = DARKGRAY;
+ }
+
+ // Draw the color handle
+ DrawCircleLinesV(circlePosition, 4.0f, handleColor);
+
+ // Draw the color in a preview, with a darkened outline.
+ DrawRectangleV((Vector2){ 8.0f, 8.0f }, (Vector2){ 64.0f, 64.0f }, color);
+ DrawRectangleLinesEx((Rectangle){ 8.0f, 8.0f, 64.0f, 64.0f }, 2.0f, ColorLerp(color, BLACK, 0.5f));
+
+ // Draw current color as hex and decimal
+ DrawText(TextFormat("#%02X%02X%02X\n(%d, %d, %d)", color.r, color.g, color.b, color.r, color.g, color.b), 8, 8 + 64 + 8, 20, DARKGRAY);
+
+ // Update the visuals for the copying text
+ Color copyColor = DARKGRAY;
+ unsigned int offset = 0;
+ if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyDown(KEY_C))
+ {
+ copyColor = DARKGREEN;
+ offset = 4;
+ }
+
+ // Draw the copying text
+ DrawText("press ctrl+c to copy!", 8, 425 - offset, 20, copyColor);
+
+ // Display the number of rendered triangles
+ DrawText(TextFormat("triangle count: %d", triangleCount), 8, 395, 20, DARKGRAY);
+
+ // Slider to change color's value
+ GuiSliderBar(sliderRectangle, "value: ", "", &value, 0.0f, 1.0f);
+
+ // Draw FPS next to outlined color preview
+ DrawFPS(64 + 16, 8);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
\ No newline at end of file
diff --git a/examples/shapes/shapes_rlgl_color_wheel.png b/examples/shapes/shapes_rlgl_color_wheel.png
new file mode 100644
index 000000000..43344da59
Binary files /dev/null and b/examples/shapes/shapes_rlgl_color_wheel.png differ
diff --git a/examples/shapes/shapes_rlgl_triangle.c b/examples/shapes/shapes_rlgl_triangle.c
new file mode 100644
index 000000000..56cdc43bc
--- /dev/null
+++ b/examples/shapes/shapes_rlgl_triangle.c
@@ -0,0 +1,169 @@
+/*******************************************************************************************
+*
+* raylib [shapes] example - rlgl triangle
+*
+* Example complexity rating: [★★☆☆] 2/4
+*
+* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+* Example contributed by Robin (@RobinsAviary) 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 Robin (@RobinsAviary)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "rlgl.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ SetConfigFlags(FLAG_MSAA_4X_HINT);
+ InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rlgl triangle");
+
+ // Starting postions and rendered triangle positions
+ Vector2 startingPositions[3] = {{ 400.0f, 150.0f }, { 300.0f, 300.0f }, { 500.0f, 300.0f }};
+ Vector2 trianglePositions[3] = { startingPositions[0], startingPositions[1], startingPositions[2] };
+
+ // Currently selected vertex, -1 means none
+ int triangleIndex = -1;
+ bool linesMode = false;
+ float handleRadius = 8.0f;
+
+ SetTargetFPS(60);
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ if (IsKeyPressed(KEY_SPACE)) linesMode = !linesMode;
+
+ // Check selected vertex
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ // If the mouse is within the handle circle
+ if (CheckCollisionPointCircle(GetMousePosition(), trianglePositions[i], handleRadius) &&
+ IsMouseButtonDown(MOUSE_BUTTON_LEFT))
+ {
+ triangleIndex = i;
+ break;
+ }
+ }
+
+ // If the user has selected a vertex, offset it by the mouse's delta this frame
+ if (triangleIndex != -1)
+ {
+ Vector2 *position = &trianglePositions[triangleIndex];
+
+ Vector2 mouseDelta = GetMouseDelta();
+ position->x += mouseDelta.x;
+ position->y += mouseDelta.y;
+ }
+
+ // Reset index on release
+ if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) triangleIndex = -1;
+
+ // Enable/disable backface culling (2-sided triangles, slower to render)
+ if (IsKeyPressed(KEY_LEFT)) rlEnableBackfaceCulling();
+ if (IsKeyPressed(KEY_RIGHT)) rlDisableBackfaceCulling();
+
+ // Reset triangle vertices to starting positions and reset backface culling
+ if (IsKeyPressed(KEY_R))
+ {
+ trianglePositions[0] = startingPositions[0];
+ trianglePositions[1] = startingPositions[1];
+ trianglePositions[2] = startingPositions[2];
+
+ rlEnableBackfaceCulling();
+ }
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ if (linesMode)
+ {
+ // Draw triangle with lines
+ rlBegin(RL_LINES);
+ // Three lines, six points
+ // Define color for next vertex
+ rlColor4ub(255, 0, 0, 255);
+ // Define vertex
+ rlVertex2f(trianglePositions[0].x, trianglePositions[0].y);
+ rlColor4ub(0, 255, 0, 255);
+ rlVertex2f(trianglePositions[1].x, trianglePositions[1].y);
+
+ rlColor4ub(0, 255, 0, 255);
+ rlVertex2f(trianglePositions[1].x, trianglePositions[1].y);
+ rlColor4ub(0, 0, 255, 255);
+ rlVertex2f(trianglePositions[2].x, trianglePositions[2].y);
+
+ rlColor4ub(0, 0, 255, 255);
+ rlVertex2f(trianglePositions[2].x, trianglePositions[2].y);
+ rlColor4ub(255, 0, 0, 255);
+ rlVertex2f(trianglePositions[0].x, trianglePositions[0].y);
+ rlEnd();
+ }
+ else
+ {
+ // Draw triangle as a triangle
+ rlBegin(RL_TRIANGLES);
+ // One triangle, three points
+ // Define color for next vertex
+ rlColor4ub(255, 0, 0, 255);
+ // Define vertex
+ rlVertex2f(trianglePositions[0].x, trianglePositions[0].y);
+ rlColor4ub(0, 255, 0, 255);
+ rlVertex2f(trianglePositions[1].x, trianglePositions[1].y);
+ rlColor4ub(0, 0, 255, 255);
+ rlVertex2f(trianglePositions[2].x, trianglePositions[2].y);
+ rlEnd();
+ }
+
+ // Render the vertex handles, reacting to mouse movement/input
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ // Draw handle fill focused by mouse
+ if (CheckCollisionPointCircle(GetMousePosition(), trianglePositions[i], handleRadius))
+ DrawCircleV(trianglePositions[i], handleRadius, ColorAlpha(DARKGRAY, 0.5f));
+
+ // Draw handle fill selected
+ if (i == triangleIndex) DrawCircleV(trianglePositions[i], handleRadius, DARKGRAY);
+
+ // Draw handle outline
+ DrawCircleLinesV(trianglePositions[i], handleRadius, BLACK);
+ }
+
+ // Draw controls
+ DrawText("SPACE: Toggle lines mode", 10, 10, 20, DARKGRAY);
+ DrawText("LEFT-RIGHT: Toggle backface culling", 10, 40, 20, DARKGRAY);
+ DrawText("MOUSE: Click and drag vertex points", 10, 70, 20, DARKGRAY);
+ DrawText("R: Reset triangle to start positions", 10, 100, 20, DARKGRAY);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
\ No newline at end of file
diff --git a/examples/shapes/shapes_rlgl_triangle.png b/examples/shapes/shapes_rlgl_triangle.png
new file mode 100644
index 000000000..9fec558ff
Binary files /dev/null and b/examples/shapes/shapes_rlgl_triangle.png differ
diff --git a/examples/shapes/shapes_simple_particles.c b/examples/shapes/shapes_simple_particles.c
index c5d9612c3..f8151e7ad 100644
--- a/examples/shapes/shapes_simple_particles.c
+++ b/examples/shapes/shapes_simple_particles.c
@@ -36,7 +36,7 @@ static const char particleTypeNames[3][10] = { "WATER", "SMOKE", "FIRE" };
typedef struct Particle {
ParticleType type; // Particle type (WATER, SMOKE, FIRE)
Vector2 position; // Particle position on screen
- Vector2 velocity; // Particle current speed and direction
+ Vector2 velocity; // Particle current speed and direction
float radius; // Particle radius
Color color; // Particle color
@@ -45,9 +45,9 @@ typedef struct Particle {
} Particle;
typedef struct CircularBuffer {
- int head; // Index for the next write
- int tail; // Index for the next read
- Particle *buffer; // Particle buffer array
+ int head; // Index for the next write
+ int tail; // Index for the next read
+ Particle *buffer; // Particle buffer array
} CircularBuffer;
//----------------------------------------------------------------------------------
@@ -73,12 +73,12 @@ int main(void)
// Definition of particles
Particle *particles = (Particle*)RL_CALLOC(MAX_PARTICLES, sizeof(Particle)); // Particle array
- CircularBuffer circularBuffer = { 0, 0, particles };
+ CircularBuffer circularBuffer = { 0, 0, particles };
- // Particle emitter parameters
+ // Particle emitter parameters
int emissionRate = -2; // Negative: on average every -X frames. Positive: particles per frame
- ParticleType currentType = WATER;
- Vector2 emitterPosition = { screenWidth/2.0f, screenHeight/2.0f };
+ ParticleType currentType = WATER;
+ Vector2 emitterPosition = { screenWidth/2.0f, screenHeight/2.0f };
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
@@ -88,17 +88,17 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
- // Emit new particles: when emissionRate is 1, emit every frame
+ // Emit new particles: when emissionRate is 1, emit every frame
if (emissionRate < 0)
{
if (rand()%(-emissionRate) == 0) EmitParticle(&circularBuffer, emitterPosition, currentType);
}
else
{
- for (int i = 0; i <= emissionRate; ++i) EmitParticle(&circularBuffer, emitterPosition, currentType);
- }
+ for (int i = 0; i <= emissionRate; i++) EmitParticle(&circularBuffer, emitterPosition, currentType);
+ }
- // Update the parameters of each particle
+ // Update the parameters of each particle
UpdateParticles(&circularBuffer, screenWidth, screenHeight);
// Remove dead particles from the circular buffer
@@ -112,7 +112,7 @@ int main(void)
if (IsKeyPressed(KEY_RIGHT)) (currentType == FIRE)? (currentType = WATER) : currentType++;
if (IsKeyPressed(KEY_LEFT)) (currentType == WATER)? (currentType = FIRE) : currentType--;
- if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) emitterPosition = GetMousePosition();
+ if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) emitterPosition = GetMousePosition();
//----------------------------------------------------------------------------------
// Draw
@@ -121,7 +121,7 @@ int main(void)
ClearBackground(RAYWHITE);
- // Call the function with a loop to draw all particles
+ // Call the function with a loop to draw all particles
DrawParticles(&circularBuffer);
// Draw UI and Instructions
@@ -133,7 +133,7 @@ int main(void)
DrawText("LEFT/RIGHT: Change Particle Type (Water, Smoke, Fire)", 15, 55, 10, BLACK);
if (emissionRate < 0) DrawText(TextFormat("Particles every %d frames | Type: %s", -emissionRate, particleTypeNames[currentType]), 15, 95, 10, DARKGRAY);
- else DrawText(TextFormat("%d Particles per frame | Type: %s", emissionRate + 1, particleTypeNames[currentType]), 15, 95, 10, DARKGRAY);
+ else DrawText(TextFormat("%d Particles per frame | Type: %s", emissionRate + 1, particleTypeNames[currentType]), 15, 95, 10, DARKGRAY);
DrawFPS(screenWidth - 80, 10);
@@ -200,12 +200,12 @@ static Particle *AddToCircularBuffer(CircularBuffer *circularBuffer)
// Check if buffer full
if (((circularBuffer->head + 1)%MAX_PARTICLES) != circularBuffer->tail)
{
- // Add new particle to the head position and advance head
+ // Add new particle to the head position and advance head
particle = &circularBuffer->buffer[circularBuffer->head];
circularBuffer->head = (circularBuffer->head + 1)%MAX_PARTICLES;
}
- return particle;
+ return particle;
}
static void UpdateParticles(CircularBuffer *circularBuffer, int screenWidth, int screenHeight)
@@ -213,7 +213,7 @@ static void UpdateParticles(CircularBuffer *circularBuffer, int screenWidth, int
for (int i = circularBuffer->tail; i != circularBuffer->head; i = (i + 1)%MAX_PARTICLES)
{
// Update particle life and positions
- circularBuffer->buffer[i].lifeTime += 1.0f/60.0f; // 60 FPS -> 1/60 seconds per frame
+ circularBuffer->buffer[i].lifeTime += 1.0f/60.0f; // 60 FPS -> 1/60 seconds per frame
switch (circularBuffer->buffer[i].type)
{
@@ -226,32 +226,32 @@ static void UpdateParticles(CircularBuffer *circularBuffer, int screenWidth, int
case SMOKE:
{
circularBuffer->buffer[i].position.x += circularBuffer->buffer[i].velocity.x;
- circularBuffer->buffer[i].velocity.y -= 0.05f; // Upwards
+ circularBuffer->buffer[i].velocity.y -= 0.05f; // Upwards
circularBuffer->buffer[i].position.y += circularBuffer->buffer[i].velocity.y;
circularBuffer->buffer[i].radius += 0.5f; // Increment radius: smoke expands
- circularBuffer->buffer[i].color.a -= 4; // Decrement alpha: smoke fades
-
+ circularBuffer->buffer[i].color.a -= 4; // Decrement alpha: smoke fades
+
// If alpha transparent, particle dies
if (circularBuffer->buffer[i].color.a < 4) circularBuffer->buffer[i].alive = false;
} break;
case FIRE:
{
- // Add a little horizontal oscillation to fire particles
+ // Add a little horizontal oscillation to fire particles
circularBuffer->buffer[i].position.x += circularBuffer->buffer[i].velocity.x + cosf(circularBuffer->buffer[i].lifeTime*215.0f);
circularBuffer->buffer[i].velocity.y -= 0.05f; // Upwards
circularBuffer->buffer[i].position.y += circularBuffer->buffer[i].velocity.y;
- circularBuffer->buffer[i].radius -= 0.15f; // Decrement radius: fire shrinks
- circularBuffer->buffer[i].color.g -= 3; // Decrement green: fire turns reddish starting from yellow
-
+ circularBuffer->buffer[i].radius -= 0.15f; // Decrement radius: fire shrinks
+ circularBuffer->buffer[i].color.g -= 3; // Decrement green: fire turns reddish starting from yellow
+
// If radius too small, particle dies
if (circularBuffer->buffer[i].radius <= 0.02f) circularBuffer->buffer[i].alive = false;
} break;
default: break;
}
- // Disable particle when out of screen
+ // Disable particle when out of screen
Vector2 center = circularBuffer->buffer[i].position;
- float radius = circularBuffer->buffer[i].radius;
+ float radius = circularBuffer->buffer[i].radius;
if ((center.x < -radius) || (center.x > (screenWidth + radius)) ||
(center.y < -radius) || (center.y > (screenHeight + radius)))
@@ -267,7 +267,7 @@ static void UpdateCircularBuffer(CircularBuffer *circularBuffer)
while ((circularBuffer->tail != circularBuffer->head) && !circularBuffer->buffer[circularBuffer->tail].alive)
{
circularBuffer->tail = (circularBuffer->tail + 1)%MAX_PARTICLES;
- }
+ }
}
static void DrawParticles(CircularBuffer *circularBuffer)
diff --git a/examples/shapes/shapes_triangle_strip.c b/examples/shapes/shapes_triangle_strip.c
index 3172e1f7f..de712270c 100644
--- a/examples/shapes/shapes_triangle_strip.c
+++ b/examples/shapes/shapes_triangle_strip.c
@@ -34,12 +34,12 @@ int main(void)
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - triangle strip");
Vector2 points[122] = { 0 };
- Vector2 center = { (screenWidth/2.0f) - 125.f, screenHeight/2.0f };
+ Vector2 center = { (screenWidth/2.0f) - 125.0f, screenHeight/2.0f };
float segments = 6.0f;
float insideRadius = 100.0f;
float outsideRadius = 150.0f;
bool outline = true;
-
+
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
@@ -79,20 +79,20 @@ int main(void)
float angle1 = i*angleStep;
DrawTriangle(c, b, a, ColorFromHSV(angle1*RAD2DEG, 1.0f, 1.0f));
DrawTriangle(d, b, c, ColorFromHSV((angle1 + angleStep/2)*RAD2DEG, 1.0f, 1.0f));
-
+
if (outline)
{
DrawTriangleLines(a, b, c, BLACK);
DrawTriangleLines(c, b, d, BLACK);
}
}
-
+
DrawLine(580, 0, 580, GetScreenHeight(), (Color){ 218, 218, 218, 255 });
DrawRectangle(580, 0, GetScreenWidth(), GetScreenHeight(), (Color){ 232, 232, 232, 255 });
// Draw GUI controls
//------------------------------------------------------------------------------
- GuiSliderBar((Rectangle){ 640, 40, 120, 20}, "Segments", TextFormat("%.0f", segments), &segments, 6.0f, 60.f);
+ GuiSliderBar((Rectangle){ 640, 40, 120, 20}, "Segments", TextFormat("%.0f", segments), &segments, 6.0f, 60.0f);
GuiCheckBox((Rectangle){ 640, 70, 20, 20 }, "Outline", &outline);
//------------------------------------------------------------------------------
diff --git a/examples/shapes/shapes_vector_angle.c b/examples/shapes/shapes_vector_angle.c
index b5faf4c78..464af1bd1 100644
--- a/examples/shapes/shapes_vector_angle.c
+++ b/examples/shapes/shapes_vector_angle.c
@@ -25,7 +25,7 @@ int main(void)
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
-
+
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - vector angle");
diff --git a/examples/text/resources/LICENSE.md b/examples/text/resources/LICENSE.md
index 506a4aaf0..91fd618fd 100644
--- a/examples/text/resources/LICENSE.md
+++ b/examples/text/resources/LICENSE.md
@@ -8,13 +8,15 @@
| fonts/mecha.png | Captain Falcon | [Freeware](https://www.dafont.com/es/mecha-cf.font) | Atlas created by [@raysan5](https://github.com/raysan5) |
| fonts/pixelplay.png | Aleksander Shevchuk | [Freeware](https://www.dafont.com/es/pixelplay.font) | Atlas created by [@raysan5](https://github.com/raysan5) |
| fonts/pixantiqua.ttf | Gerhard Großmann | [Freeware](https://www.dafont.com/es/pixantiqua.font) | Atlas created by [@raysan5](https://github.com/raysan5) |
-| anonymous_pro_bold.ttf | [Mark Simonson](https://fonts.google.com/specimen/Anonymous+Pro) | [Open Font License](https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL) | - |
+| anonymous_pro_bold.ttf | [Mark Simonson](https://fonts.google.com/specimen/Anonymous+Pro) | [SIL Open Font License](https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL) | - |
| custom_alagard.png | [Brian Kent (AEnigma)](https://www.dafont.com/es/aenigma.d188) | [Freeware](https://www.dafont.com/es/jupiter-crash.font) | Atlas created by [@raysan5](https://github.com/raysan5) |
| custom_jupiter_crash.png | [Brian Kent (AEnigma)](https://www.dafont.com/es/aenigma.d188) | [Freeware](https://www.dafont.com/es/jupiter-crash.font) | Atlas created by [@raysan5](https://github.com/raysan5) |
| custom_mecha.png | [Brian Kent (AEnigma)](https://www.dafont.com/es/aenigma.d188) | [Freeware](https://www.dafont.com/es/jupiter-crash.font) | Atlas created by [@raysan5](https://github.com/raysan5) |
| dejavu.fnt, dejavu.png | [DejaVu Fonts](https://dejavu-fonts.github.io/) | [Free](https://dejavu-fonts.github.io/License.html) | Atlas made with [BMFont](https://www.angelcode.com/products/bmfont/) by [@raysan5](https://github.com/raysan5) |
| KAISG.ttf | [Dieter Steffmann](http://www.steffmann.de/wordpress/) | [Freeware](https://www.1001fonts.com/users/steffmann/) | [Kaiserzeit Gotisch](https://www.dafont.com/es/kaiserzeit-gotisch.font) font |
-| noto_cjk.fnt, noto_cjk.png | [Google Fonts](https://www.google.com/get/noto/help/cjk/) | [Open Font License](https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL) | Atlas made with [BMFont](https://www.angelcode.com/products/bmfont/) by [@raysan5](https://github.com/raysan5) |
+| noto_cjk.fnt, noto_cjk.png | [Google Fonts](https://www.google.com/get/noto/help/cjk/) | [SIL Open Font License](https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL) | Atlas made with [BMFont](https://www.angelcode.com/products/bmfont/) by [@raysan5](https://github.com/raysan5) |
| pixantiqua.fnt, pixantiqua.png | Gerhard Großmann | [Freeware](https://www.dafont.com/es/pixantiqua.font) | Atlas made with [BMFont](https://www.angelcode.com/products/bmfont/) by [@raysan5](https://github.com/raysan5) |
| pixantiqua.ttf | Gerhard Großmann | [Freeware](https://www.dafont.com/es/pixantiqua.font) | - |
| symbola.fnt, symbola.png | George Douros | [Freeware](https://fontlibrary.org/en/font/symbola) | Atlas made with [BMFont](https://www.angelcode.com/products/bmfont/) by [@raysan5](https://github.com/raysan5) |
+| DotGothic16-Regular.ttf | [The DotGothic16 Project Authors](https://github.com/fontworks-fonts/DotGothic16) | [Open Font License](https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL) | - |
+| NotoSansTC-Regular.ttf | [Adobe](http://www.adobe.com/) | [SIL Open Font License](https://openfontlicense.org/documents/OFL.txt) | - |
diff --git a/examples/text/text_3d_drawing.c b/examples/text/text_3d_drawing.c
index 202494bf7..80b617b2e 100644
--- a/examples/text/text_3d_drawing.c
+++ b/examples/text/text_3d_drawing.c
@@ -231,7 +231,7 @@ int main(void)
if (multicolor)
{
// Fill color array with random colors
- for (int i = 0; i < TEXT_MAX_LAYERS; ++i)
+ for (int i = 0; i < TEXT_MAX_LAYERS; i++)
{
multi[i] = GenerateRandomColor(0.5f, 0.8f);
multi[i].a = GetRandomValue(0, 255);
@@ -296,7 +296,7 @@ int main(void)
rlRotatef(90.0f, 1.0f, 0.0f, 0.0f);
rlRotatef(90.0f, 0.0f, 0.0f, -1.0f);
- for (int i = 0; i < layers; ++i)
+ for (int i = 0; i < layers; i++)
{
Color clr = light;
if (multicolor) clr = multi[i];
diff --git a/examples/text/text_inline_styling.c b/examples/text/text_inline_styling.c
index b221c7e6f..8faef30eb 100644
--- a/examples/text/text_inline_styling.c
+++ b/examples/text/text_inline_styling.c
@@ -36,11 +36,11 @@ int main(void)
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [text] example - inline styling");
-
+
Vector2 textSize = { 0 }; // Measure text box for provided font and text
Color colRandom = RED; // Random color used on text
int frameCounter = 0; // Used to generate a new random color every certain frames
-
+
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
@@ -50,7 +50,7 @@ int main(void)
// Update
//----------------------------------------------------------------------------------
frameCounter++;
-
+
if ((frameCounter%20) == 0)
{
colRandom.r = (unsigned char)GetRandomValue(0, 255);
@@ -67,12 +67,14 @@ int main(void)
ClearBackground(RAYWHITE);
// Text inline styling strategy used: [ ] delimiters for format
- // - Define foreground color: [cRRGGBBAA]
+ // - Define foreground color: [cRRGGBBAA]
// - Define background color: [bRRGGBBAA]
// - Reset formating: [r]
+ // Colors defined with [cRRGGBBAA] or [bRRGGBBAA] are multiplied by the base color alpha
+ // This allows global transparency control while keeping per-section styling (ex. text fade effects)
// Example: [bAA00AAFF][cFF0000FF]red text on gray background[r] normal text
-
- DrawTextStyled(GetFontDefault(), "This changes the [cFF0000FF]foreground color[r] of provided text!!!",
+
+ DrawTextStyled(GetFontDefault(), "This changes the [cFF0000FF]foreground color[r] of provided text!!!",
(Vector2){ 100, 80 }, 20.0f, 2.0f, BLACK);
DrawTextStyled(GetFontDefault(), "This changes the [bFF00FFFF]background color[r] of provided text!!!",
@@ -80,13 +82,16 @@ int main(void)
DrawTextStyled(GetFontDefault(), "This changes the [c00ff00ff][bff0000ff]foreground and background colors[r]!!!",
(Vector2){ 100, 160 }, 20.0f, 2.0f, BLACK);
-
+
+ DrawTextStyled(GetFontDefault(), "This changes the [c00ff00ff]alpha[r] relative [cffffffff][b000000ff]from source[r] [cff000088]color[r]!!!",
+ (Vector2){ 100, 200 }, 20.0f, 2.0f, (Color){ 0, 0, 0, 100 });
+
// Get pointer to formated text
const char *text = TextFormat("Let's be [c%02x%02x%02xFF]CREATIVE[r] !!!", colRandom.r, colRandom.g, colRandom.b);
- DrawTextStyled(GetFontDefault(), text, (Vector2){ 100, 220 }, 40.0f, 2.0f, BLACK);
-
+ DrawTextStyled(GetFontDefault(), text, (Vector2){ 100, 240 }, 40.0f, 2.0f, BLACK);
+
textSize = MeasureTextStyled(GetFontDefault(), text, 40.0f, 2.0f);
- DrawRectangleLines(100, 220, (int)textSize.x, (int)textSize.y, GREEN);
+ DrawRectangleLines(100, 240, (int)textSize.x, (int)textSize.y, GREEN);
EndDrawing();
//----------------------------------------------------------------------------------
@@ -105,16 +110,17 @@ int main(void)
//----------------------------------------------------------------------------------
// Draw text using inline styling
// PARAM: color is the default text color, background color is BLANK by default
+// NOTE: Using input color as the base alpha multiplied to inline styles
static void DrawTextStyled(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color color)
{
// Text inline styling strategy used: [ ] delimiters for format
- // - Define foreground color: [cRRGGBBAA]
+ // - Define foreground color: [cRRGGBBAA]
// - Define background color: [bRRGGBBAA]
// - Reset formating: [r]
// Example: [bAA00AAFF][cFF0000FF]red text on gray background[r] normal text
-
+
if (font.texture.id == 0) font = GetFontDefault();
-
+
int textLen = TextLength(text);
Color colFront = color;
@@ -144,14 +150,14 @@ static void DrawTextStyled(Font font, const char *text, Vector2 position, float
{
colFront = color;
colBack = BLANK;
-
+
i += 3; // Skip "[r]"
continue; // Do not draw characters
}
else if (((i + 1) < textLen) && ((text[i + 1] == 'c') || (text[i + 1] == 'b')))
{
i += 2; // Skip "[c" or "[b" to start parsing color
-
+
// Parse following color
char colHexText[9] = { 0 };
const char *textPtr = &text[i]; // Color should start here, let's see...
@@ -168,12 +174,20 @@ static void DrawTextStyled(Font font, const char *text, Vector2 position, float
}
else break; // Only affects while loop
}
-
+
// Convert hex color text into actual Color
unsigned int colHexValue = strtoul(colHexText, NULL, 16);
- if (text[i - 1] == 'c') colFront = GetColor(colHexValue);
- else if (text[i - 1] == 'b') colBack = GetColor(colHexValue);
-
+ if (text[i - 1] == 'c')
+ {
+ colFront = GetColor(colHexValue);
+ colFront.a *= (float)color.a/255.0f;
+ }
+ else if (text[i - 1] == 'b')
+ {
+ colBack = GetColor(colHexValue);
+ colBack.a *= (float)color.a/255.0f;
+ }
+
i += (colHexCount + 1); // Skip color value retrieved and ']'
continue; // Do not draw characters
}
@@ -186,7 +200,7 @@ static void DrawTextStyled(Font font, const char *text, Vector2 position, float
else increaseX += ((float)font.glyphs[index].advanceX*scaleFactor + spacing);
// Draw background rectangle color (if required)
- if (colBack.a > 0) DrawRectangleRec((Rectangle) { position.x + textOffsetX, position.y + textOffsetY - backRecPadding, increaseX, fontSize + 2 * backRecPadding }, colBack);
+ if (colBack.a > 0) DrawRectangleRec((Rectangle) { position.x + textOffsetX, position.y + textOffsetY - backRecPadding, increaseX, fontSize + 2*backRecPadding }, colBack);
if ((codepoint != ' ') && (codepoint != '\t'))
{
@@ -210,7 +224,7 @@ static Vector2 MeasureTextStyled(Font font, const char *text, float fontSize, fl
if ((font.texture.id == 0) || (text == NULL) || (text[0] == '\0')) return textSize; // Security check
int textLen = TextLength(text); // Get size in bytes of text
- float textLineSpacing = fontSize*1.5f;
+ //float textLineSpacing = fontSize*1.5f; // Not used...
float textWidth = 0.0f;
float textHeight = fontSize;
@@ -249,7 +263,7 @@ static Vector2 MeasureTextStyled(Font font, const char *text, float fontSize, fl
}
else break; // Only affects while loop
}
-
+
i += (colHexCount + 1); // Skip color value retrieved and ']'
continue; // Do not measure characters
}
@@ -260,7 +274,7 @@ static Vector2 MeasureTextStyled(Font font, const char *text, float fontSize, fl
if (font.glyphs[index].advanceX > 0) textWidth += font.glyphs[index].advanceX;
else textWidth += (font.recs[index].width + font.glyphs[index].offsetX);
-
+
validCodepointCounter++;
i += codepointByteCount;
}
diff --git a/examples/text/text_strings_management.c b/examples/text/text_strings_management.c
new file mode 100644
index 000000000..d2e349279
--- /dev/null
+++ b/examples/text/text_strings_management.c
@@ -0,0 +1,400 @@
+/*******************************************************************************************
+*
+* raylib [text] example - strings management
+*
+* Example complexity rating: [★★★☆] 3/4
+*
+* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+* Example contributed by David Buzatto (@davidbuzatto) 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 David Buzatto (@davidbuzatto)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include
+
+#define MAX_TEXT_LENGTH 100
+#define MAX_TEXT_PARTICLES 100
+#define FONT_SIZE 30
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef struct TextParticle {
+ char text[MAX_TEXT_LENGTH];
+ Rectangle rect; // Boundary
+ Vector2 vel; // Velocity
+ Vector2 ppos; // Previous position
+ float padding;
+ float borderWidth;
+ float friction;
+ float elasticity;
+ Color color;
+ bool grabbed;
+} TextParticle;
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+void PrepareFirstTextParticle(const char* text, TextParticle *tps, int *particleCount);
+TextParticle CreateTextParticle(const char *text, float x, float y, Color color);
+void SliceTextParticle(TextParticle *tp, int particlePos, int sliceLength, TextParticle *tps, int *particleCount);
+void SliceTextParticleByChar(TextParticle *tp, char charToSlice, TextParticle *tps, int *particleCount);
+void ShatterTextParticle(TextParticle *tp, int particlePos, TextParticle *tps, int *particleCount);
+void GlueTextParticles(TextParticle *grabbed, TextParticle *target, TextParticle *tps, int *particleCount);
+void RealocateTextParticles(TextParticle *tps, int particlePos, int *particleCount);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [text] example - strings management");
+
+ TextParticle textParticles[MAX_TEXT_PARTICLES] = { 0 };
+ int particleCount = 0;
+ TextParticle *grabbedTextParticle = NULL;
+ Vector2 pressOffset = {0};
+
+ PrepareFirstTextParticle("raylib => fun videogames programming!", textParticles, &particleCount);
+
+ 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
+ //----------------------------------------------------------------------------------
+ float delta = GetFrameTime();
+ Vector2 mousePos = GetMousePosition();
+
+ // Checks if a text particle was grabbed
+ if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
+ {
+ for (int i = particleCount - 1; i >= 0; i--)
+ {
+ TextParticle *tp = &textParticles[i];
+ pressOffset.x = mousePos.x - tp->rect.x;
+ pressOffset.y = mousePos.y - tp->rect.y;
+ if (CheckCollisionPointRec(mousePos, tp->rect))
+ {
+ tp->grabbed = true;
+ grabbedTextParticle = tp;
+ break;
+ }
+ }
+ }
+
+ // Releases any text particle the was grabbed
+ if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT))
+ {
+ if (grabbedTextParticle != NULL)
+ {
+ grabbedTextParticle->grabbed = false;
+ grabbedTextParticle = NULL;
+ }
+ }
+
+ // Slice os shatter a text particle
+ if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT))
+ {
+ for (int i = particleCount - 1; i >= 0; i--)
+ {
+ TextParticle *tp = &textParticles[i];
+ if (CheckCollisionPointRec(mousePos, tp->rect))
+ {
+ if (IsKeyDown(KEY_LEFT_SHIFT))
+ {
+ ShatterTextParticle(tp, i, textParticles, &particleCount);
+ }
+ else
+ {
+ SliceTextParticle(tp, i, TextLength(tp->text)/2, textParticles, &particleCount);
+ }
+ break;
+ }
+ }
+ }
+
+ // Shake text particles
+ if (IsMouseButtonPressed(MOUSE_BUTTON_MIDDLE))
+ {
+ for (int i = 0; i < particleCount; i++)
+ {
+ if (!textParticles[i].grabbed) textParticles[i].vel = (Vector2){ GetRandomValue(-2000, 2000), GetRandomValue(-2000, 2000) };
+ }
+ }
+
+ // Reset using TextTo* functions
+ if (IsKeyPressed(KEY_ONE)) PrepareFirstTextParticle("raylib => fun videogames programming!", textParticles, &particleCount);
+ if (IsKeyPressed(KEY_TWO)) PrepareFirstTextParticle(TextToUpper("raylib => fun videogames programming!"), textParticles, &particleCount);
+ if (IsKeyPressed(KEY_THREE)) PrepareFirstTextParticle(TextToLower("raylib => fun videogames programming!"), textParticles, &particleCount);
+ if (IsKeyPressed(KEY_FOUR)) PrepareFirstTextParticle(TextToPascal("raylib_fun_videogames_programming"), textParticles, &particleCount);
+ if (IsKeyPressed(KEY_FIVE)) PrepareFirstTextParticle(TextToSnake("RaylibFunVideogamesProgramming"), textParticles, &particleCount);
+ if (IsKeyPressed(KEY_SIX)) PrepareFirstTextParticle(TextToCamel("raylib_fun_videogames_programming"), textParticles, &particleCount);
+
+ // Slice by char pressed only when we have one text particle
+ char charPressed = GetCharPressed();
+ if ((charPressed >= 'A') && (charPressed <= 'z') && (particleCount == 1))
+ {
+ SliceTextParticleByChar(&textParticles[0], charPressed, textParticles, &particleCount);
+ }
+
+ // Updates each text particle state
+ for (int i = 0; i < particleCount; i++)
+ {
+ TextParticle *tp = &textParticles[i];
+
+ // The text particle is not grabbed
+ if (!tp->grabbed)
+ {
+ // text particle repositioning using the velocity
+ tp->rect.x += tp->vel.x * delta;
+ tp->rect.y += tp->vel.y * delta;
+
+ // Does the text particle hit the screen right boundary?
+ if ((tp->rect.x + tp->rect.width) >= screenWidth)
+ {
+ tp->rect.x = screenWidth - tp->rect.width; // Text particle repositioning
+ tp->vel.x = -tp->vel.x*tp->elasticity; // Elasticity makes the text particle lose 10% of its velocity on hit
+ }
+ // Does the text particle hit the screen left boundary?
+ else if (tp->rect.x <= 0)
+ {
+ tp->rect.x = 0.0f;
+ tp->vel.x = -tp->vel.x*tp->elasticity;
+ }
+
+ // The same for y axis
+ if ((tp->rect.y + tp->rect.height) >= screenHeight)
+ {
+ tp->rect.y = screenHeight - tp->rect.height;
+ tp->vel.y = -tp->vel.y*tp->elasticity;
+ }
+ else if (tp->rect.y <= 0)
+ {
+ tp->rect.y = 0.0f;
+ tp->vel.y = -tp->vel.y*tp->elasticity;
+ }
+
+ // Friction makes the text particle lose 1% of its velocity each frame
+ tp->vel.x = tp->vel.x*tp->friction;
+ tp->vel.y = tp->vel.y*tp->friction;
+ }
+ else
+ {
+ // Text particle repositioning using the mouse position
+ tp->rect.x = mousePos.x - pressOffset.x;
+ tp->rect.y = mousePos.y - pressOffset.y;
+
+ // While the text particle is grabbed, recalculates its velocity
+ tp->vel.x = (tp->rect.x - tp->ppos.x)/delta;
+ tp->vel.y = (tp->rect.y - tp->ppos.y)/delta;
+ tp->ppos.x = tp->rect.x;
+ tp->ppos.y = tp->rect.y;
+
+ // Glue text particles when dragging and pressing left ctrl
+ if (IsKeyDown(KEY_LEFT_CONTROL))
+ {
+ for (int i = 0; i < particleCount; i++)
+ {
+ if (&textParticles[i] != grabbedTextParticle && grabbedTextParticle->grabbed)
+ {
+ if (CheckCollisionRecs(grabbedTextParticle->rect, textParticles[i].rect))
+ {
+ GlueTextParticles(grabbedTextParticle, &textParticles[i], textParticles, &particleCount);
+ grabbedTextParticle = &textParticles[particleCount-1];
+ }
+ }
+ }
+ }
+ }
+ }
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ 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(tp->rect, tp->color);
+ DrawText(tp->text, tp->rect.x+tp->padding, 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);
+ DrawText("slice a text particle by pressing it with the mouse right button", 10, 30, 10, DARKGRAY);
+ DrawText("shatter a text particle keeping left shift pressed and pressing it with the mouse right button", 10, 50, 10, DARKGRAY);
+ DrawText("glue text particles by grabbing than and keeping left control pressed", 10, 70, 10, DARKGRAY);
+ DrawText("1 to 6 to reset", 10, 90, 10, DARKGRAY);
+ DrawText("when you have only one text particle, you can slice it by pressing a char", 10, 110, 10, DARKGRAY);
+ DrawText(TextFormat("TEXT PARTICLE COUNT: %d", particleCount), 10, GetScreenHeight() - 30, 20, BLACK);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+void PrepareFirstTextParticle(const char* text, TextParticle *tps, int *particleCount)
+{
+ tps[0] = CreateTextParticle(
+ text,
+ GetScreenWidth()/2,
+ GetScreenHeight()/2,
+ RAYWHITE
+ );
+ *particleCount = 1;
+}
+
+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) },
+ .ppos = { 0 },
+ .padding = 5.0f,
+ .borderWidth = 5.0f,
+ .friction = 0.99,
+ .elasticity = 0.9,
+ .color = color,
+ .grabbed = false
+ };
+
+ TextCopy(tp.text, text);
+ tp.rect.width = MeasureText(tp.text, FONT_SIZE)+tp.padding*2;
+ tp.rect.height = FONT_SIZE+tp.padding*2;
+ return tp;
+}
+
+void SliceTextParticle(TextParticle *tp, int particlePos, int sliceLength, TextParticle *tps, int *particleCount)
+{
+ int length = TextLength(tp->text);
+
+ if((length > 1) && ((*particleCount+length) < MAX_TEXT_PARTICLES))
+ {
+ for (int i = 0; i < length; i += sliceLength)
+ {
+ const char *text = sliceLength == 1 ? TextFormat("%c", tp->text[i]) : TextSubtext(tp->text, i, sliceLength);
+ tps[(*particleCount)++] = CreateTextParticle(
+ text,
+ tp->rect.x + i * tp->rect.width/length,
+ tp->rect.y,
+ (Color) { GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }
+ );
+ }
+ RealocateTextParticles(tps, particlePos, particleCount);
+ }
+}
+
+void SliceTextParticleByChar(TextParticle *tp, char charToSlice, TextParticle *tps, int *particleCount)
+{
+ int tokenCount = 0;
+ char **tokens = TextSplit(tp->text, charToSlice, &tokenCount);
+
+ if (tokenCount > 1)
+ {
+ int textLength = TextLength(tp->text);
+ for (int i = 0; i < textLength; i++)
+ {
+ if (tp->text[i] == charToSlice)
+ {
+ tps[(*particleCount)++] = CreateTextParticle(
+ TextFormat("%c", charToSlice),
+ tp->rect.x,
+ tp->rect.y,
+ (Color) { GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }
+ );
+ }
+ }
+ for (int i = 0; i < tokenCount; i++)
+ {
+ int tokenLength = TextLength(tokens[i]);
+ tps[(*particleCount)++] = CreateTextParticle(
+ TextFormat("%s", tokens[i]),
+ tp->rect.x + i * tp->rect.width/tokenLength,
+ tp->rect.y,
+ (Color) { GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }
+ );
+ }
+ if (tokenCount)
+ {
+ RealocateTextParticles(tps, 0, particleCount);
+ }
+ }
+}
+
+void ShatterTextParticle(TextParticle *tp, int particlePos, TextParticle *tps, int *particleCount)
+{
+ SliceTextParticle(tp, particlePos, 1, tps, particleCount);
+}
+
+void GlueTextParticles(TextParticle *grabbed, TextParticle *target, TextParticle *tps, int *particleCount)
+{
+ int p1 = -1;
+ int p2 = -1;
+
+ for (int i = 0; i < *particleCount; i++)
+ {
+ if (&tps[i] == grabbed) p1 = i;
+ if (&tps[i] == target) p2 = i;
+ }
+
+ if ((p1 != -1) && (p2 != -1))
+ {
+ TextParticle tp = CreateTextParticle(
+ TextFormat( "%s%s", grabbed->text, target->text),
+ grabbed->rect.x,
+ grabbed->rect.y,
+ RAYWHITE
+ );
+ tp.grabbed = true;
+ tps[(*particleCount)++] = tp;
+ grabbed->grabbed = false;
+ if (p1 < p2)
+ {
+ RealocateTextParticles(tps, p2, particleCount);
+ RealocateTextParticles(tps, p1, particleCount);
+ }
+ else
+ {
+ RealocateTextParticles(tps, p1, particleCount);
+ RealocateTextParticles(tps, p2, particleCount);
+ }
+ }
+}
+
+void RealocateTextParticles(TextParticle *tps, int particlePos, int *particleCount)
+{
+ for (int i = particlePos+1; i < *particleCount; i++)
+ {
+ tps[i-1] = tps[i];
+ }
+ (*particleCount)--;
+}
\ No newline at end of file
diff --git a/examples/text/text_strings_management.png b/examples/text/text_strings_management.png
new file mode 100644
index 000000000..d9b6cc4ed
Binary files /dev/null and b/examples/text/text_strings_management.png differ
diff --git a/examples/text/text_unicode_emojis.c b/examples/text/text_unicode_emojis.c
index 30fcaa8b7..00712745d 100644
--- a/examples/text/text_unicode_emojis.c
+++ b/examples/text/text_unicode_emojis.c
@@ -210,7 +210,7 @@ int main(void)
// Draw random emojis in the background
//------------------------------------------------------------------------------
- for (int i = 0; i < SIZEOF(emoji); ++i)
+ for (int i = 0; i < SIZEOF(emoji); i++)
{
const char *txt = &emojiCodepoints[emoji[i].index];
Rectangle emojiRect = { position.x, position.y, (float)fontEmoji.baseSize, (float)fontEmoji.baseSize };
@@ -316,7 +316,7 @@ static void RandomizeEmoji(void)
hovered = selected = -1;
int start = GetRandomValue(45, 360);
- for (int i = 0; i < SIZEOF(emoji); ++i)
+ for (int i = 0; i < SIZEOF(emoji); i++)
{
// 0-179 emoji codepoints (from emoji char array) each 4bytes + null char
emoji[i].index = GetRandomValue(0, 179)*5;
diff --git a/examples/text/text_unicode_ranges.c b/examples/text/text_unicode_ranges.c
index e54c052b1..76ee75f8d 100644
--- a/examples/text/text_unicode_ranges.c
+++ b/examples/text/text_unicode_ranges.c
@@ -58,7 +58,7 @@ int main(void)
// Load font with default Unicode range: Basic ASCII [32-127]
font = LoadFont("resources/NotoSansTC-Regular.ttf");
-
+
// Add required ranges to loaded font
switch (unicodeRange)
{
@@ -128,11 +128,11 @@ int main(void)
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
-
+
ClearBackground(RAYWHITE);
DrawText("ADD CODEPOINTS: [1][2][3][4]", 20, 20, 20, MAROON);
-
+
// Render test strings in different languages
DrawTextEx(font, "> English: Hello World!", (Vector2){ 50, 70 }, 32, 1, DARKGRAY); // English
DrawTextEx(font, "> Español: Hola mundo!", (Vector2){ 50, 120 }, 32, 1, DARKGRAY); // Spanish
@@ -141,7 +141,7 @@ int main(void)
DrawTextEx(font, "> 中文: 你好世界!", (Vector2){ 50, 270 }, 32, 1, DARKGRAY); // Chinese
DrawTextEx(font, "> 日本語: こんにちは世界!", (Vector2){ 50, 320 }, 32, 1, DARKGRAY); // Japanese
//DrawTextEx(font, "देवनागरी: होला मुंडो!", (Vector2){ 50, 350 }, 32, 1, DARKGRAY); // Devanagari (glyphs not available in font)
-
+
// Draw font texture scaled to screen
float atlasScale = 380.0f/font.texture.width;
DrawRectangleRec((Rectangle) { 400.0f, 16.0f, font.texture.width* atlasScale, font.texture.height* atlasScale }, BLACK);
@@ -161,7 +161,7 @@ int main(void)
DrawRectangle(0, 125, screenWidth, 200, GRAY);
DrawText("GENERATING FONT ATLAS...", 120, 210, 40, BLACK);
}
-
+
EndDrawing();
//----------------------------------------------------------------------------------
}
@@ -184,10 +184,10 @@ static void AddCodepointRange(Font *font, const char *fontPath, int start, int s
{
int rangeSize = stop - start + 1;
int currentRangeSize = font->glyphCount;
-
+
// TODO: Load glyphs from provided vector font (if available),
// add them to existing font, regenerating font image and texture
-
+
int updatedCodepointCount = currentRangeSize + rangeSize;
int *updatedCodepoints = (int *)RL_CALLOC(updatedCodepointCount, sizeof(int));
diff --git a/examples/text/text_words_alignment.c b/examples/text/text_words_alignment.c
index 352b3cd37..a558d5b11 100644
--- a/examples/text/text_words_alignment.c
+++ b/examples/text/text_words_alignment.c
@@ -19,6 +19,8 @@
#include "raymath.h" // Required for: Lerp()
+#include
+
typedef enum TextAlignment {
TEXT_ALIGN_LEFT = 0,
TEXT_ALIGN_TOP = 0,
@@ -39,26 +41,26 @@ int main(void)
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [text] example - words alignment");
-
+
// Define the rectangle we will draw the text in
Rectangle textContainerRect = (Rectangle){ screenWidth/2-screenWidth/4, screenHeight/2-screenHeight/3, screenWidth/2, screenHeight*2/3 };
// Some text to display the current alignment
const char *textAlignNameH[] = { "Left", "Centre", "Right" };
const char *textAlignNameV[] = { "Top", "Middle", "Bottom" };
-
+
// Define the text we're going to draw in the rectangle
int wordIndex = 0;
int wordCount = 0;
char **words = TextSplit("raylib is a simple and easy-to-use library to enjoy videogames programming", ' ', &wordCount);
-
+
// Initialize the font size we're going to use
int fontSize = 40;
-
+
// And of course the font...
Font font = GetFontDefault();
-
- // Intialize the alignment variables
+
+ // Initialize the alignment variables
TextAlignment hAlign = TEXT_ALIGN_CENTRE;
TextAlignment vAlign = TEXT_ALIGN_MIDDLE;
@@ -70,27 +72,31 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
-
- if (IsKeyPressed(KEY_LEFT)) {
- hAlign = hAlign - 1;
- if (hAlign < 0) hAlign = 0;
+ if (IsKeyPressed(KEY_LEFT))
+ {
+ if (hAlign > 0) hAlign = hAlign - 1;
}
- if (IsKeyPressed(KEY_RIGHT)) {
+
+ if (IsKeyPressed(KEY_RIGHT))
+ {
hAlign = hAlign + 1;
if (hAlign > 2) hAlign = 2;
}
- if (IsKeyPressed(KEY_UP)) {
- vAlign = vAlign - 1;
- if (vAlign < 0) vAlign = 0;
+
+ if (IsKeyPressed(KEY_UP))
+ {
+ if (vAlign > 0) vAlign = vAlign - 1;
}
- if (IsKeyPressed(KEY_DOWN)) {
+
+ if (IsKeyPressed(KEY_DOWN))
+ {
vAlign = vAlign + 1;
if (vAlign > 2) vAlign = 2;
}
-
+
// One word per second
- wordIndex = (int)GetTime() % wordCount;
-
+ if (wordCount > 0) wordIndex = (int)GetTime()%wordCount;
+ else wordIndex = 0;
//----------------------------------------------------------------------------------
// Draw
@@ -103,16 +109,16 @@ int main(void)
DrawText(TextFormat("Alignment: Horizontal = %s, Vertical = %s", textAlignNameH[hAlign], textAlignNameV[vAlign]), 20, 40, 20, LIGHTGRAY);
DrawRectangleRec(textContainerRect, BLUE);
-
+
// Get the size of the text to draw
Vector2 textSize = MeasureTextEx(font, words[wordIndex], fontSize, fontSize*.1f);
-
+
// Calculate the top-left text position based on the rectangle and alignment
- Vector2 textPos = (Vector2) {
- textContainerRect.x + Lerp(0.0f, textContainerRect.width - textSize.x, ((float)hAlign) * 0.5f),
- textContainerRect.y + Lerp(0.0f, textContainerRect.height - textSize.y, ((float)vAlign) * 0.5f)
+ Vector2 textPos = (Vector2){
+ textContainerRect.x + Lerp(0.0f, textContainerRect.width - textSize.x, ((float)hAlign)*0.5f),
+ textContainerRect.y + Lerp(0.0f, textContainerRect.height - textSize.y, ((float)vAlign)*0.5f)
};
-
+
// Draw the text
DrawTextEx(font, words[wordIndex], textPos, fontSize, fontSize*.1f, RAYWHITE);
@@ -127,4 +133,4 @@ int main(void)
//--------------------------------------------------------------------------------------
return 0;
-}
\ No newline at end of file
+}
diff --git a/examples/textures/resources/LICENSE.md b/examples/textures/resources/LICENSE.md
index e4ee45304..7d67572c7 100644
--- a/examples/textures/resources/LICENSE.md
+++ b/examples/textures/resources/LICENSE.md
@@ -8,7 +8,7 @@
| explosion.png | [Unity Labs Paris](https://blogs.unity3d.com/2016/11/28/free-vfx-image-sequences-flipbooks/) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) |
| parrots.png | [Kodak set](http://r0k.us/graphics/kodak/) | ❔ | Original name: `kodim23.png`
| cat.png | ❔ | ❔ | - |
-| wabbit_alpha.png | ❔ | ❔ | - |
+| raybunny.png | [VoidSrc*](https://x.com/voidsrc) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - |
| custom_jupiter_crash.png | [Brian Kent (AEnigma)](https://www.dafont.com/es/aenigma.d188) | [Freeware](https://www.dafont.com/es/jupiter-crash.font) | Atlas created by [@raysan5](https://github.com/raysan5) |
| KAISG.ttf | [Dieter Steffmann](http://www.steffmann.de/wordpress/) | [Freeware](https://www.1001fonts.com/users/steffmann/) | [Kaiserzeit Gotisch](https://www.dafont.com/es/kaiserzeit-gotisch.font) font |
| fudesumi.png | [Eiden Marsal](https://www.artstation.com/marshall_z) | [CC-BY-NC](https://creativecommons.org/licenses/by-nc/4.0/) | - |
diff --git a/examples/textures/resources/booth.png b/examples/textures/resources/booth.png
new file mode 100644
index 000000000..b4147f77b
Binary files /dev/null and b/examples/textures/resources/booth.png differ
diff --git a/examples/textures/resources/raybunny.png b/examples/textures/resources/raybunny.png
new file mode 100644
index 000000000..b608147bc
Binary files /dev/null and b/examples/textures/resources/raybunny.png differ
diff --git a/examples/textures/resources/wabbit_alpha.png b/examples/textures/resources/wabbit_alpha.png
deleted file mode 100644
index db4081fec..000000000
Binary files a/examples/textures/resources/wabbit_alpha.png and /dev/null differ
diff --git a/examples/textures/textures_bunnymark.c b/examples/textures/textures_bunnymark.c
index 7789602b5..a95181ed6 100644
--- a/examples/textures/textures_bunnymark.c
+++ b/examples/textures/textures_bunnymark.c
@@ -45,7 +45,7 @@ int main(void)
InitWindow(screenWidth, screenHeight, "raylib [textures] example - bunnymark");
// Load bunny texture
- Texture2D texBunny = LoadTexture("resources/wabbit_alpha.png");
+ Texture2D texBunny = LoadTexture("resources/raybunny.png");
Bunny *bunnies = (Bunny *)malloc(MAX_BUNNIES*sizeof(Bunny)); // Bunnies array
diff --git a/examples/textures/textures_bunnymark.png b/examples/textures/textures_bunnymark.png
index 4431ccec4..74ecfb115 100644
Binary files a/examples/textures/textures_bunnymark.png and b/examples/textures/textures_bunnymark.png differ
diff --git a/examples/textures/textures_cellular_automata.c b/examples/textures/textures_cellular_automata.c
new file mode 100644
index 000000000..affeeda93
--- /dev/null
+++ b/examples/textures/textures_cellular_automata.c
@@ -0,0 +1,212 @@
+/*******************************************************************************************
+*
+* raylib [textures] example - cellular automata
+*
+* Example complexity rating: [★★☆☆] 2/4
+*
+* Example originally created with raylib 5.6, last time updated with raylib 5.6
+*
+* Example contributed by Jordi Santonja (@JordSant) 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 Jordi Santonja (@JordSant)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+// Initialization constants
+//--------------------------------------------------------------------------------------
+const int screenWidth = 800;
+const int screenHeight = 450;
+const int imageWidth = 800;
+const int imageHeight = 800/2;
+
+// Rule button sizes and positions
+const int drawRuleStartX = 585;
+const int drawRuleStartY = 10;
+const int drawRuleSpacing = 15;
+const int drawRuleGroupSpacing = 50;
+const int drawRuleSize = 14;
+const int drawRuleInnerSize = 10;
+
+// Preset button sizes
+const int presetsSizeX = 42;
+const int presetsSizeY = 22;
+
+const int linesUpdatedPerFrame = 4;
+
+//----------------------------------------------------------------------------------
+// Functions
+//----------------------------------------------------------------------------------
+void ComputeLine(Image *image, int line, int rule)
+{
+ // Compute next line pixels. Boundaries are not computed, always 0
+ for (int i = 1; i < imageWidth - 1; i++)
+ {
+ // Get, from the previous line, the 3 pixels states as a binary value
+ const int prevValue = ((GetImageColor(*image, i - 1, line - 1).r < 5)? 4 : 0) + // Left pixel
+ ((GetImageColor(*image, i, line - 1).r < 5)? 2 : 0) + // Center pixel
+ ((GetImageColor(*image, i + 1, line - 1).r < 5)? 1 : 0); // Right pixel
+ // Get next value from rule bitmask
+ const bool currValue = (rule & (1 << prevValue));
+ // Update pixel color
+ ImageDrawPixel(image, i, line, (currValue)? BLACK : RAYWHITE);
+ }
+}
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ InitWindow(screenWidth, screenHeight, "raylib [textures] example - cellular automata");
+
+ // Image that contains the cellular automaton
+ Image image = GenImageColor(imageWidth, imageHeight, RAYWHITE);
+ // The top central pixel set as black
+ ImageDrawPixel(&image, imageWidth/2, 0, BLACK);
+
+ Texture2D texture = LoadTextureFromImage(image);
+
+ // Some interesting rules
+ const int presetValues[] = { 18, 30, 60, 86, 102, 124, 126, 150, 182, 225 };
+ const int presetsCount = sizeof(presetValues)/sizeof(presetValues[0]);
+
+ // Variables
+ int rule = 30; // Starting rule
+ int line = 1; // Line to compute, starting from line 1. One point in line 0 is already set
+
+ SetTargetFPS(60);
+ //---------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ // Handle mouse
+ const Vector2 mouse = GetMousePosition();
+ int mouseInCell = -1; // -1: outside any button; 0-7: rule cells; 8+: preset cells
+
+ // Check mouse on rule cells
+ for (int i = 0; i < 8; i++)
+ {
+ const int cellX = drawRuleStartX - drawRuleGroupSpacing*i + drawRuleSpacing;
+ const int cellY = drawRuleStartY + drawRuleSpacing;
+ if ((mouse.x >= cellX) && (mouse.x <= cellX + drawRuleSize) &&
+ (mouse.y >= cellY) && (mouse.y <= cellY + drawRuleSize))
+ {
+ mouseInCell = i; // 0-7: rule cells
+ break;
+ }
+ }
+
+ // Check mouse on preset cells
+ if (mouseInCell < 0)
+ {
+ for (int i = 0; i < presetsCount; i++)
+ {
+ const int cellX = 4 + (presetsSizeX + 2)*(i/2);
+ const int cellY = 2 + (presetsSizeY + 2)*(i%2);
+ if ((mouse.x >= cellX) && (mouse.x <= cellX + presetsSizeX) &&
+ (mouse.y >= cellY) && (mouse.y <= cellY + presetsSizeY))
+ {
+ mouseInCell = i + 8; // 8+: preset cells
+ break;
+ }
+ }
+ }
+
+ if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && (mouseInCell >= 0))
+ {
+ // Rule changed both by selecting a preset or toggling a bit
+ if (mouseInCell < 8)
+ rule ^= (1 << mouseInCell);
+ else
+ rule = presetValues[mouseInCell - 8];
+
+ // Reset image
+ ImageClearBackground(&image, RAYWHITE);
+ ImageDrawPixel(&image, imageWidth/2, 0, BLACK);
+ line = 1;
+ }
+
+ // Compute next lines
+ //----------------------------------------------------------------------------------
+ if (line < imageHeight)
+ {
+ for (int i = 0; (i < linesUpdatedPerFrame) && (line + i < imageHeight); i++)
+ ComputeLine(&image, line + i, rule);
+ line += linesUpdatedPerFrame;
+
+ UpdateTexture(texture, image.data);
+ }
+
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+ ClearBackground(RAYWHITE);
+
+ // Draw cellular automaton texture
+ DrawTexture(texture, 0, screenHeight - imageHeight, WHITE);
+
+ // Draw preset values
+ for (int i = 0; i < presetsCount; i++)
+ {
+ DrawText(TextFormat("%i", presetValues[i]), 8 + (presetsSizeX + 2)*(i/2), 4 + (presetsSizeY + 2)*(i%2), 20, GRAY);
+ DrawRectangleLines(4 + (presetsSizeX + 2)*(i/2), 2 + (presetsSizeY + 2)*(i%2), presetsSizeX, presetsSizeY, BLUE);
+
+ // If the mouse is on this preset, highlight it
+ if (mouseInCell == i + 8)
+ DrawRectangleLinesEx((Rectangle) { 2 + (presetsSizeX + 2.0f)*(i/2),
+ (presetsSizeY + 2.0f)*(i%2),
+ presetsSizeX + 4.0f, presetsSizeY + 4.0f }, 3, RED);
+ }
+
+ // Draw rule bits
+ for (int i = 0; i < 8; i++)
+ {
+ // The three input bits
+ for (int j = 0; j < 3; j++)
+ {
+ DrawRectangleLines(drawRuleStartX - drawRuleGroupSpacing*i + drawRuleSpacing*j, drawRuleStartY, drawRuleSize, drawRuleSize, GRAY);
+ if (i & (4 >> j))
+ DrawRectangle(drawRuleStartX + 2 - drawRuleGroupSpacing*i + drawRuleSpacing*j, drawRuleStartY + 2, drawRuleInnerSize, drawRuleInnerSize, BLACK);
+ }
+
+ // The output bit
+ DrawRectangleLines(drawRuleStartX - drawRuleGroupSpacing*i + drawRuleSpacing, drawRuleStartY + drawRuleSpacing, drawRuleSize, drawRuleSize, BLUE);
+ if (rule & (1 << i))
+ DrawRectangle(drawRuleStartX + 2 - drawRuleGroupSpacing*i + drawRuleSpacing, drawRuleStartY + 2 + drawRuleSpacing, drawRuleInnerSize, drawRuleInnerSize, BLACK);
+
+ // If the mouse is on this rule bit, highlight it
+ if (mouseInCell == i)
+ DrawRectangleLinesEx((Rectangle){ drawRuleStartX - drawRuleGroupSpacing*i + drawRuleSpacing - 2.0f,
+ drawRuleStartY + drawRuleSpacing - 2.0f,
+ drawRuleSize + 4.0f, drawRuleSize + 4.0f }, 3, RED);
+ }
+
+ DrawText(TextFormat("RULE: %i", rule), drawRuleStartX + drawRuleSpacing*4, drawRuleStartY + 1, 30, GRAY);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ UnloadImage(image);
+ UnloadTexture(texture);
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
+
diff --git a/examples/textures/textures_cellular_automata.png b/examples/textures/textures_cellular_automata.png
new file mode 100644
index 000000000..2d88041d2
Binary files /dev/null and b/examples/textures/textures_cellular_automata.png differ
diff --git a/examples/textures/textures_mouse_painting.c b/examples/textures/textures_mouse_painting.c
index 2575c4450..6996ddac4 100644
--- a/examples/textures/textures_mouse_painting.c
+++ b/examples/textures/textures_mouse_painting.c
@@ -179,7 +179,7 @@ int main(void)
ClearBackground(RAYWHITE);
// NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom)
- DrawTextureRec(target.texture, (Rectangle) { 0, 0, (float)target.texture.width, (float)-target.texture.height }, (Vector2) { 0, 0 }, WHITE);
+ DrawTextureRec(target.texture, (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height }, (Vector2) { 0, 0 }, WHITE);
// Draw drawing circle for reference
if (mousePos.y > 50)
diff --git a/examples/textures/textures_screen_buffer.c b/examples/textures/textures_screen_buffer.c
new file mode 100644
index 000000000..e620aab31
--- /dev/null
+++ b/examples/textures/textures_screen_buffer.c
@@ -0,0 +1,150 @@
+/*******************************************************************************************
+*
+* raylib [textures] example - screen buffer
+*
+* Example complexity rating: [★★☆☆] 2/4
+*
+* Example originally created with raylib 5.5, last time updated with raylib 5.5
+*
+* Example contributed by Agnis Aldiņš (@nezvers) 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 Agnis Aldiņš (@nezvers)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include // Required for: calloc(), free()
+
+#define MAX_COLORS 256
+#define SCALE_FACTOR 2
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [textures] example - screen buffer");
+
+ int imageWidth = screenWidth/SCALE_FACTOR;
+ int imageHeight = screenHeight/SCALE_FACTOR;
+ int flameWidth = screenWidth/SCALE_FACTOR;
+
+ Color palette[MAX_COLORS] = { 0 };
+ unsigned char *indexBuffer = RL_CALLOC(imageWidth*imageWidth, sizeof(unsigned char));
+ unsigned char *flameRootBuffer = RL_CALLOC(flameWidth, sizeof(unsigned char));
+
+ Image screenImage = GenImageColor(imageWidth, imageHeight, BLACK);
+ Texture screenTexture = LoadTextureFromImage(screenImage);
+
+ // Generate flame color palette
+ for (int i = 0; i < MAX_COLORS; i++)
+ {
+ float t = (float)i/(float)(MAX_COLORS - 1);
+ float hue = t*t;
+ float saturation = t;
+ float value = t;
+ palette[i] = ColorFromHSV(250.0f + 150.0f*hue, saturation, value);
+ }
+
+ SetTargetFPS(60);
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ // Grow flameRoot
+ for (int x = 2; x < flameWidth; x++)
+ {
+ unsigned short flame = flameRootBuffer[x];
+ if (flame == 255) continue;
+ flame += GetRandomValue(0, 2);
+ if (flame > 255) flame = 255;
+ flameRootBuffer[x] = flame;
+ }
+
+ // Transfer flameRoot to indexBuffer
+ for (int x = 0; x < flameWidth; x++)
+ {
+ int i = x + (imageHeight - 1)*imageWidth;
+ indexBuffer[i] = flameRootBuffer[x];
+ }
+
+ // Clear top row, because it can't move any higher
+ for (int x = 0; x < imageWidth; x++)
+ {
+ if (indexBuffer[x] == 0) continue;
+ indexBuffer[x] = 0;
+ }
+
+ // Skip top row, it is already cleared
+ for (int y = 1; y < imageHeight; y++)
+ {
+ for (int x = 0; x < imageWidth; x++)
+ {
+ unsigned int i = x + y*imageWidth;
+ unsigned char colorIndex = indexBuffer[i];
+ if (colorIndex == 0) continue;
+
+ // Move pixel a row above
+ indexBuffer[i] = 0;
+ int moveX = GetRandomValue(0, 2) - 1;
+ int newX = x + moveX;
+ if (newX < 0 || newX >= imageWidth) continue;
+
+ unsigned int iabove = i - imageWidth + moveX;
+ int decay = GetRandomValue(0, 3);
+ colorIndex -= (decay < colorIndex)? decay : colorIndex;
+ indexBuffer[iabove] = colorIndex;
+ }
+ }
+
+ // Update screenImage with palette colors
+ for (int y = 1; y < imageHeight; y++)
+ {
+ for (int x = 0; x < imageWidth; x++)
+ {
+ unsigned int i = x + y*imageWidth;
+ unsigned char colorIndex = indexBuffer[i];
+ Color col = palette[colorIndex];
+ ImageDrawPixel(&screenImage, x, y, col);
+ }
+ }
+
+ UpdateTexture(screenTexture, screenImage.data);
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ DrawTextureEx(screenTexture, (Vector2){ 0, 0 }, 0.0f, 2.0f, WHITE);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ RL_FREE(indexBuffer);
+ RL_FREE(flameRootBuffer);
+ UnloadTexture(screenTexture);
+ UnloadImage(screenImage);
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
diff --git a/examples/textures/textures_screen_buffer.png b/examples/textures/textures_screen_buffer.png
new file mode 100644
index 000000000..b93bba989
Binary files /dev/null and b/examples/textures/textures_screen_buffer.png differ
diff --git a/examples/textures/textures_sprite_stacking.c b/examples/textures/textures_sprite_stacking.c
new file mode 100644
index 000000000..b20474711
--- /dev/null
+++ b/examples/textures/textures_sprite_stacking.c
@@ -0,0 +1,105 @@
+/*******************************************************************************************
+*
+* raylib [textures] example - sprite stacking
+*
+* Example complexity rating: [★★☆☆] 2/4
+*
+* Example originally created with raylib 5.6-dev, last time updated with raylib 6.0
+*
+* Example contributed by Robin (@RobinsAviary) 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
+*
+* Redbooth model (c) 2017-2025 @kluchek under https://creativecommons.org/licenses/by/4.0/ https://github.com/kluchek/vox-models/
+* Copyright (c) 2025 Robin (@RobinsAviary)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h" // Required for: Clamp()
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [textures] example - sprite stacking");
+
+ Texture2D booth = LoadTexture("resources/booth.png");
+
+ float stackScale = 3.0f; // Overall scale of the stacked sprite
+ float stackSpacing = 2.0f; // Vertical spacing between each layer
+ unsigned int stackCount = 122; // Number of layers, used for calculating the size of a single slice
+ float rotationSpeed = 30.0f; // Stacked sprites rotation speed
+ float rotation = 0.0f; // Current rotation of the stacked sprite
+ const float speedChange = 0.25f; // Amount speed will change by when the user presses A/D
+
+ SetTargetFPS(60);
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ // Use mouse wheel to affect stack separation
+ stackSpacing += GetMouseWheelMove()*0.1f;
+ stackSpacing = Clamp(stackSpacing, 0.0f, 5.0f);
+
+ // Add a positive/negative offset to spin right/left at different speeds
+ if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) rotationSpeed -= speedChange;
+ if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) rotationSpeed += speedChange;
+
+ rotation += rotationSpeed*GetFrameTime();
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ // Get the size of a single slice
+ float frameWidth = (float)booth.width;
+ float frameHeight = (float)booth.height/(float)stackCount;
+
+ // Get the scaled resolution to draw at
+ float scaledWidth = frameWidth*stackScale;
+ float scaledHeight = frameHeight*stackScale;
+
+ // Draw the stacked sprite, rotated to the correct angle, with an vertical offset applied based on its y location
+ for (int i = stackCount - 1; i >= 0; i--)
+ {
+ // Center vertically
+ Rectangle source = { 0.0f, (float)i*frameHeight, frameWidth, frameHeight };
+ Rectangle dest = { screenWidth/2.0f, (screenHeight/2.0f) + (i*stackSpacing) - (stackSpacing*stackCount/2.0f), scaledWidth, scaledHeight };
+ Vector2 origin = { scaledWidth/2.0f, scaledHeight/2.0f };
+
+ DrawTexturePro(booth, source, dest, origin, rotation, WHITE);
+ }
+
+ DrawText("A/D to spin\nmouse wheel to change separation (aka 'angle')", 10, 10, 20, DARKGRAY);
+ DrawText(TextFormat("current spacing: %.01f", stackSpacing), 10, 50, 20, DARKGRAY);
+ DrawText(TextFormat("current speed: %.02f", rotationSpeed), 10, 70, 20, DARKGRAY);
+ DrawText("redbooth model (c) kluchek under cc 4.0", 10, 420, 20, DARKGRAY);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ UnloadTexture(booth);
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
\ No newline at end of file
diff --git a/examples/textures/textures_sprite_stacking.png b/examples/textures/textures_sprite_stacking.png
new file mode 100644
index 000000000..53b986823
Binary files /dev/null and b/examples/textures/textures_sprite_stacking.png differ
diff --git a/examples/textures/textures_textured_curve.c b/examples/textures/textures_textured_curve.c
index feeefc7b6..abf78c88a 100644
--- a/examples/textures/textures_textured_curve.c
+++ b/examples/textures/textures_textured_curve.c
@@ -49,7 +49,7 @@ static void DrawTexturedCurve(void);
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
-int main()
+int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
diff --git a/examples/textures/textures_tiled_drawing.c b/examples/textures/textures_tiled_drawing.c
index 10bcad1ad..9e5285035 100644
--- a/examples/textures/textures_tiled_drawing.c
+++ b/examples/textures/textures_tiled_drawing.c
@@ -40,7 +40,7 @@ int main(void)
// NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
Texture texPattern = LoadTexture("resources/patterns.png");
- SetTextureFilter(texPattern, TEXTURE_FILTER_TRILINEAR); // Makes the texture smoother when upscaled
+ SetTextureFilter(texPattern, TEXTURE_FILTER_BILINEAR); // Makes the texture smoother when upscaled
// Coordinates for all patterns inside the texture
const Rectangle recPattern[] = {
@@ -100,7 +100,7 @@ int main(void)
}
// Check to see which color was clicked and set it as the active color
- for (int i = 0; i < MAX_COLORS; ++i)
+ for (int i = 0; i < MAX_COLORS; i++)
{
if (CheckCollisionPointRec(mouse, colorRec[i]))
{
@@ -110,19 +110,17 @@ int main(void)
}
}
- // Handle keys
-
- // Change scale
+ // Handle keys: change scale
if (IsKeyPressed(KEY_UP)) scale += 0.25f;
if (IsKeyPressed(KEY_DOWN)) scale -= 0.25f;
if (scale > 10.0f) scale = 10.0f;
else if ( scale <= 0.0f) scale = 0.25f;
- // Change rotation
+ // Handle keys: change rotation
if (IsKeyPressed(KEY_LEFT)) rotation -= 25.0f;
if (IsKeyPressed(KEY_RIGHT)) rotation += 25.0f;
- // Reset
+ // Handle keys: reset
if (IsKeyPressed(KEY_SPACE)) { rotation = 0.0f; scale = 1.0f; }
//----------------------------------------------------------------------------------
@@ -165,7 +163,7 @@ int main(void)
// De-Initialization
//--------------------------------------------------------------------------------------
- UnloadTexture(texPattern); // Unload texture
+ UnloadTexture(texPattern); // Unload texture
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
diff --git a/projects/4coder/Makefile.Android b/projects/4coder/Makefile.Android
index 29d437b1b..9e6773651 100644
--- a/projects/4coder/Makefile.Android
+++ b/projects/4coder/Makefile.Android
@@ -96,7 +96,7 @@ ifeq ($(ANDROID_ARCH),ARM)
CFLAGS = -std=c99 -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16
endif
ifeq ($(ANDROID_ARCH),ARM64)
- CFLAGS = -std=c99 -target aarch64 -mfix-cortex-a53-835769
+ CFLAGS = -std=c99 -mfix-cortex-a53-835769
endif
# Compilation functions attributes options
CFLAGS += -ffunction-sections -funwind-tables -fstack-protector-strong -fPIC
diff --git a/projects/VS2022/examples/audio_spectrum_visualizer.vcxproj b/projects/VS2022/examples/audio_spectrum_visualizer.vcxproj
new file mode 100644
index 000000000..f8e5005fd
--- /dev/null
+++ b/projects/VS2022/examples/audio_spectrum_visualizer.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
+
+
+
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}
+ Win32Proj
+ audio_spectrum_visualizer
+ 10.0
+ audio_spectrum_visualizer
+
+
+
+ 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\audio
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\audio
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\audio
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\audio
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\audio
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\audio
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\audio
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\audio
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\audio
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\audio
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\audio
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\audio
+ 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/core_compute_hash.vcxproj b/projects/VS2022/examples/core_compute_hash.vcxproj
new file mode 100644
index 000000000..bbca36d86
--- /dev/null
+++ b/projects/VS2022/examples/core_compute_hash.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
+
+
+
+ {6C897101-BE52-4387-8AA2-062123A76BA1}
+ Win32Proj
+ core_compute_hash
+ 10.0
+ core_compute_hash
+
+
+
+ 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/core_high_dpi.vcxproj b/projects/VS2022/examples/core_highdpi_demo.vcxproj
similarity index 97%
rename from projects/VS2022/examples/core_high_dpi.vcxproj
rename to projects/VS2022/examples/core_highdpi_demo.vcxproj
index 11e1b41c0..8a7cdce40 100644
--- a/projects/VS2022/examples/core_high_dpi.vcxproj
+++ b/projects/VS2022/examples/core_highdpi_demo.vcxproj
@@ -1,569 +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
-
-
-
- {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}
- Win32Proj
- core_high_dpi
- 10.0
- core_high_dpi
-
-
-
- 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}
-
-
-
-
-
-
+
+
+
+
+ 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
+
+
+
+ {BCB71111-8505-4B35-8CEF-EC6115DC9D4D}
+ Win32Proj
+ core_highdpi_demo
+ 10.0
+ core_highdpi_demo
+
+
+
+ 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}
+
+
+
+
+
+
diff --git a/projects/VS2022/examples/core_viewport_scaling.vcxproj b/projects/VS2022/examples/core_viewport_scaling.vcxproj
new file mode 100644
index 000000000..712cfe2a4
--- /dev/null
+++ b/projects/VS2022/examples/core_viewport_scaling.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
+
+
+
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}
+ Win32Proj
+ core_viewport_scaling
+ 10.0
+ core_viewport_scaling
+
+
+
+ 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/models_directional_billboard.vcxproj b/projects/VS2022/examples/models_directional_billboard.vcxproj
new file mode 100644
index 000000000..2e6c0e8b2
--- /dev/null
+++ b/projects/VS2022/examples/models_directional_billboard.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
+
+
+
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}
+ Win32Proj
+ models_directional_billboard
+ 10.0
+ models_directional_billboard
+
+
+
+ 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\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ 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/shaders_game_of_life.vcxproj b/projects/VS2022/examples/shaders_game_of_life.vcxproj
new file mode 100644
index 000000000..0ede87cda
--- /dev/null
+++ b/projects/VS2022/examples/shaders_game_of_life.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
+
+
+
+ {071E64F3-1396-4A97-97CA-98CAC059B168}
+ Win32Proj
+ shaders_game_of_life
+ 10.0
+ shaders_game_of_life
+
+
+
+ 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\shaders
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shaders
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shaders
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shaders
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shaders
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shaders
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shaders
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shaders
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shaders
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shaders
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shaders
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shaders
+ 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
+ _CRT_SECURE_NO_WARNINGS;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
+ _CRT_SECURE_NO_WARNINGS;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
+ _CRT_SECURE_NO_WARNINGS;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
+ _CRT_SECURE_NO_WARNINGS;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_ball_physics.vcxproj b/projects/VS2022/examples/shapes_ball_physics.vcxproj
new file mode 100644
index 000000000..9b7e98658
--- /dev/null
+++ b/projects/VS2022/examples/shapes_ball_physics.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
+
+
+
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}
+ Win32Proj
+ shapes_ball_physics
+ 10.0
+ shapes_ball_physics
+
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ /FS %(AdditionalOptions)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ /FS %(AdditionalOptions)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+
+
+
+
+
+
+ {e89d61ac-55de-4482-afd4-df7242ebc859}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/projects/VS2022/examples/shapes_penrose_tile.vcxproj b/projects/VS2022/examples/shapes_penrose_tile.vcxproj
new file mode 100644
index 000000000..bde99f8c1
--- /dev/null
+++ b/projects/VS2022/examples/shapes_penrose_tile.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
+
+
+
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}
+ Win32Proj
+ shapes_penrose_tile
+ 10.0
+ shapes_penrose_tile
+
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ /FS %(AdditionalOptions)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ /FS %(AdditionalOptions)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+
+
+
+
+
+
+ {e89d61ac-55de-4482-afd4-df7242ebc859}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/projects/VS2022/examples/shapes_rlgl_color_wheel.vcxproj b/projects/VS2022/examples/shapes_rlgl_color_wheel.vcxproj
new file mode 100644
index 000000000..b22703577
--- /dev/null
+++ b/projects/VS2022/examples/shapes_rlgl_color_wheel.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
+
+
+
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}
+ Win32Proj
+ shapes_rlgl_color_wheel
+ 10.0
+ shapes_rlgl_color_wheel
+
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ /FS %(AdditionalOptions)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ /FS %(AdditionalOptions)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+
+
+
+
+
+
+ {e89d61ac-55de-4482-afd4-df7242ebc859}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/projects/VS2022/examples/shapes_rlgl_triangle.vcxproj b/projects/VS2022/examples/shapes_rlgl_triangle.vcxproj
new file mode 100644
index 000000000..780f514a0
--- /dev/null
+++ b/projects/VS2022/examples/shapes_rlgl_triangle.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
+
+
+
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}
+ Win32Proj
+ shapes_rlgl_triangle
+ 10.0
+ shapes_rlgl_triangle
+
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\shapes
+ WindowsLocalDebugger
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ /FS %(AdditionalOptions)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ /FS %(AdditionalOptions)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+
+
+
+
+
+
+ {e89d61ac-55de-4482-afd4-df7242ebc859}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/projects/VS2022/examples/shapes_triangle_strip.vcxproj b/projects/VS2022/examples/shapes_triangle_strip.vcxproj
index b128c0ff6..eb4e200dd 100644
--- a/projects/VS2022/examples/shapes_triangle_strip.vcxproj
+++ b/projects/VS2022/examples/shapes_triangle_strip.vcxproj
@@ -553,7 +553,7 @@
-
+
diff --git a/projects/VS2022/examples/text_strings_management.vcxproj b/projects/VS2022/examples/text_strings_management.vcxproj
new file mode 100644
index 000000000..41b9b3ac0
--- /dev/null
+++ b/projects/VS2022/examples/text_strings_management.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
+
+
+
+ {1F4722E7-F78E-413F-A106-D3490211EA57}
+ Win32Proj
+ text_strings_management
+ 10.0
+ text_strings_management
+
+
+
+ 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\text
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\text
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\text
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\text
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\text
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\text
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\text
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\text
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\text
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\text
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\text
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\text
+ 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/textures_cellular_automata.vcxproj b/projects/VS2022/examples/textures_cellular_automata.vcxproj
new file mode 100644
index 000000000..f8b434a6d
--- /dev/null
+++ b/projects/VS2022/examples/textures_cellular_automata.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
+
+
+
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}
+ Win32Proj
+ textures_cellular_automata
+ 10.0
+ textures_cellular_automata
+
+
+
+ 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/examples/textures_screen_buffer.vcxproj b/projects/VS2022/examples/textures_screen_buffer.vcxproj
new file mode 100644
index 000000000..1697481ce
--- /dev/null
+++ b/projects/VS2022/examples/textures_screen_buffer.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
+
+
+
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}
+ Win32Proj
+ textures_screen_buffer
+ 10.0
+ textures_screen_buffer
+
+
+
+ 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/examples/textures_sprite_stacking.vcxproj b/projects/VS2022/examples/textures_sprite_stacking.vcxproj
new file mode 100644
index 000000000..a3e5be045
--- /dev/null
+++ b/projects/VS2022/examples/textures_sprite_stacking.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
+
+
+
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}
+ Win32Proj
+ textures_sprite_stacking
+ 10.0
+ textures_sprite_stacking
+
+
+
+ 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 c15632182..50fef1cf5 100644
--- a/projects/VS2022/raylib.sln
+++ b/projects/VS2022/raylib.sln
@@ -57,7 +57,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_custom_logging", "exam
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_drop_files", "examples\core_drop_files.vcxproj", "{0199E349-0701-40BC-8A7F-06A54FFA3E7C}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_high_dpi", "examples\core_high_dpi.vcxproj", "{BCB71111-8505-4B35-8CEF-EC6115DC9D4D}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_highdpi_demo", "examples\core_highdpi_demo.vcxproj", "{BCB71111-8505-4B35-8CEF-EC6115DC9D4D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_gamepad", "examples\core_input_gamepad.vcxproj", "{8F19E3DA-8929-4000-87B5-3CA6929636CC}"
EndProject
@@ -405,6 +405,32 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_decals", "examples\m
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_lines_drawing", "examples\shapes_lines_drawing.vcxproj", "{666346D7-C84B-498D-AE17-53B20C62DB1A}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_viewport_scaling", "examples\core_viewport_scaling.vcxproj", "{AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_compute_hash", "examples\core_compute_hash.vcxproj", "{6C897101-BE52-4387-8AA2-062123A76BA1}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_screen_buffer", "examples\textures_screen_buffer.vcxproj", "{4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_spectrum_visualizer", "examples\audio_spectrum_visualizer.vcxproj", "{2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_directional_billboard", "examples\models_directional_billboard.vcxproj", "{30011884-25EE-42C9-BB15-888CAFB1AA6E}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_rlgl_color_wheel", "examples\shapes_rlgl_color_wheel.vcxproj", "{32FE2658-1D70-442E-8672-0AC5C6F0BD7B}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_rlgl_triangle", "examples\shapes_rlgl_triangle.vcxproj", "{842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_sprite_stacking", "examples\textures_sprite_stacking.vcxproj", "{FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_ball_physics", "examples\shapes_ball_physics.vcxproj", "{0653AFAF-5578-4C02-AF29-0C873E7634AE}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_game_of_life", "examples\shaders_game_of_life.vcxproj", "{071E64F3-1396-4A97-97CA-98CAC059B168}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_penrose_tile", "examples\shapes_penrose_tile.vcxproj", "{7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_strings_management", "examples\text_strings_management.vcxproj", "{1F4722E7-F78E-413F-A106-D3490211EA57}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "textures_cellular_automata", "examples\textures_cellular_automata.vcxproj", "{0A0FC982-6E31-401F-BA77-3C5E8AB02C68}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug.DLL|ARM64 = Debug.DLL|ARM64
@@ -5027,6 +5053,318 @@ Global
{666346D7-C84B-498D-AE17-53B20C62DB1A}.Release|x64.Build.0 = Release|x64
{666346D7-C84B-498D-AE17-53B20C62DB1A}.Release|x86.ActiveCfg = Release|Win32
{666346D7-C84B-498D-AE17-53B20C62DB1A}.Release|x86.Build.0 = Release|Win32
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|x64.Build.0 = Debug.DLL|x64
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|ARM64.Build.0 = Debug|ARM64
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|x64.ActiveCfg = Debug|x64
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|x64.Build.0 = Debug|x64
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|x86.ActiveCfg = Debug|Win32
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Debug|x86.Build.0 = Debug|Win32
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|x64.ActiveCfg = Release.DLL|x64
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|x64.Build.0 = Release.DLL|x64
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release.DLL|x86.Build.0 = Release.DLL|Win32
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|ARM64.ActiveCfg = Release|ARM64
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|ARM64.Build.0 = Release|ARM64
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|x64.ActiveCfg = Release|x64
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|x64.Build.0 = Release|x64
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|x86.ActiveCfg = Release|Win32
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91}.Release|x86.Build.0 = Release|Win32
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|x64.Build.0 = Debug.DLL|x64
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|ARM64.Build.0 = Debug|ARM64
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|x64.ActiveCfg = Debug|x64
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|x64.Build.0 = Debug|x64
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|x86.ActiveCfg = Debug|Win32
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Debug|x86.Build.0 = Debug|Win32
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|x64.ActiveCfg = Release.DLL|x64
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|x64.Build.0 = Release.DLL|x64
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Release.DLL|x86.Build.0 = Release.DLL|Win32
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|ARM64.ActiveCfg = Release|ARM64
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|ARM64.Build.0 = Release|ARM64
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|x64.ActiveCfg = Release|x64
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|x64.Build.0 = Release|x64
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|x86.ActiveCfg = Release|Win32
+ {6C897101-BE52-4387-8AA2-062123A76BA1}.Release|x86.Build.0 = Release|Win32
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|x64.Build.0 = Debug.DLL|x64
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|ARM64.Build.0 = Debug|ARM64
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|x64.ActiveCfg = Debug|x64
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|x64.Build.0 = Debug|x64
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|x86.ActiveCfg = Debug|Win32
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Debug|x86.Build.0 = Debug|Win32
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|x64.ActiveCfg = Release.DLL|x64
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|x64.Build.0 = Release.DLL|x64
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release.DLL|x86.Build.0 = Release.DLL|Win32
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|ARM64.ActiveCfg = Release|ARM64
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|ARM64.Build.0 = Release|ARM64
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|x64.ActiveCfg = Release|x64
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|x64.Build.0 = Release|x64
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|x86.ActiveCfg = Release|Win32
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD}.Release|x86.Build.0 = Release|Win32
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|x64.Build.0 = Debug.DLL|x64
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|ARM64.Build.0 = Debug|ARM64
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|x64.ActiveCfg = Debug|x64
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|x64.Build.0 = Debug|x64
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|x86.ActiveCfg = Debug|Win32
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Debug|x86.Build.0 = Debug|Win32
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|x64.ActiveCfg = Release.DLL|x64
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|x64.Build.0 = Release.DLL|x64
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release.DLL|x86.Build.0 = Release.DLL|Win32
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|ARM64.ActiveCfg = Release|ARM64
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|ARM64.Build.0 = Release|ARM64
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|x64.ActiveCfg = Release|x64
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|x64.Build.0 = Release|x64
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|x86.ActiveCfg = Release|Win32
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68}.Release|x86.Build.0 = Release|Win32
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|x64.Build.0 = Debug.DLL|x64
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|ARM64.Build.0 = Debug|ARM64
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|x64.ActiveCfg = Debug|x64
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|x64.Build.0 = Debug|x64
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|x86.ActiveCfg = Debug|Win32
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Debug|x86.Build.0 = Debug|Win32
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|x64.ActiveCfg = Release.DLL|x64
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|x64.Build.0 = Release.DLL|x64
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release.DLL|x86.Build.0 = Release.DLL|Win32
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|ARM64.ActiveCfg = Release|ARM64
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|ARM64.Build.0 = Release|ARM64
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|x64.ActiveCfg = Release|x64
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|x64.Build.0 = Release|x64
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|x86.ActiveCfg = Release|Win32
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E}.Release|x86.Build.0 = Release|Win32
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|ARM64.Build.0 = Debug|ARM64
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|x64.ActiveCfg = Debug|x64
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|x64.Build.0 = Debug|x64
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|x86.ActiveCfg = Debug|Win32
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Debug|x86.Build.0 = Debug|Win32
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|x64.Build.0 = Release.DLL|x64
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release.DLL|x86.Build.0 = Release.DLL|Win32
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|ARM64.ActiveCfg = Release|ARM64
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|ARM64.Build.0 = Release|ARM64
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|x64.ActiveCfg = Release|x64
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|x64.Build.0 = Release|x64
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|x86.ActiveCfg = Release|Win32
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B}.Release|x86.Build.0 = Release|Win32
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|ARM64.Build.0 = Debug|ARM64
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|x64.ActiveCfg = Debug|x64
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|x64.Build.0 = Debug|x64
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|x86.ActiveCfg = Debug|Win32
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Debug|x86.Build.0 = Debug|Win32
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|x64.Build.0 = Release.DLL|x64
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release.DLL|x86.Build.0 = Release.DLL|Win32
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|ARM64.ActiveCfg = Release|ARM64
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|ARM64.Build.0 = Release|ARM64
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|x64.ActiveCfg = Release|x64
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|x64.Build.0 = Release|x64
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|x86.ActiveCfg = Release|Win32
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F}.Release|x86.Build.0 = Release|Win32
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|ARM64.Build.0 = Debug|ARM64
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|x64.ActiveCfg = Debug|x64
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|x64.Build.0 = Debug|x64
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|x86.ActiveCfg = Debug|Win32
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Debug|x86.Build.0 = Debug|Win32
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|x64.Build.0 = Release.DLL|x64
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release.DLL|x86.Build.0 = Release.DLL|Win32
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|ARM64.ActiveCfg = Release|ARM64
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|ARM64.Build.0 = Release|ARM64
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|x64.ActiveCfg = Release|x64
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|x64.Build.0 = Release|x64
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|x86.ActiveCfg = Release|Win32
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F}.Release|x86.Build.0 = Release|Win32
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|x64.Build.0 = Debug.DLL|x64
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|ARM64.Build.0 = Debug|ARM64
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|x64.ActiveCfg = Debug|x64
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|x64.Build.0 = Debug|x64
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|x86.ActiveCfg = Debug|Win32
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Debug|x86.Build.0 = Debug|Win32
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|x64.ActiveCfg = Release.DLL|x64
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|x64.Build.0 = Release.DLL|x64
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release.DLL|x86.Build.0 = Release.DLL|Win32
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|ARM64.ActiveCfg = Release|ARM64
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|ARM64.Build.0 = Release|ARM64
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|x64.ActiveCfg = Release|x64
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|x64.Build.0 = Release|x64
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|x86.ActiveCfg = Release|Win32
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE}.Release|x86.Build.0 = Release|Win32
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|x64.Build.0 = Debug.DLL|x64
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|ARM64.Build.0 = Debug|ARM64
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|x64.ActiveCfg = Debug|x64
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|x64.Build.0 = Debug|x64
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|x86.ActiveCfg = Debug|Win32
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Debug|x86.Build.0 = Debug|Win32
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|x64.ActiveCfg = Release.DLL|x64
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|x64.Build.0 = Release.DLL|x64
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Release.DLL|x86.Build.0 = Release.DLL|Win32
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|ARM64.ActiveCfg = Release|ARM64
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|ARM64.Build.0 = Release|ARM64
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|x64.ActiveCfg = Release|x64
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|x64.Build.0 = Release|x64
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|x86.ActiveCfg = Release|Win32
+ {071E64F3-1396-4A97-97CA-98CAC059B168}.Release|x86.Build.0 = Release|Win32
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|ARM64.Build.0 = Debug|ARM64
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|x64.ActiveCfg = Debug|x64
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|x64.Build.0 = Debug|x64
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|x86.ActiveCfg = Debug|Win32
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Debug|x86.Build.0 = Debug|Win32
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|x64.Build.0 = Release.DLL|x64
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release.DLL|x86.Build.0 = Release.DLL|Win32
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|ARM64.ActiveCfg = Release|ARM64
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|ARM64.Build.0 = Release|ARM64
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|x64.ActiveCfg = Release|x64
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|x64.Build.0 = Release|x64
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|x86.ActiveCfg = Release|Win32
+ {7883D076-CA8F-4FF7-8B5D-0DFF41CEF8FC}.Release|x86.Build.0 = Release|Win32
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|x64.Build.0 = Debug.DLL|x64
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|ARM64.Build.0 = Debug|ARM64
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|x64.ActiveCfg = Debug|x64
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|x64.Build.0 = Debug|x64
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|x86.ActiveCfg = Debug|Win32
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Debug|x86.Build.0 = Debug|Win32
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|x64.ActiveCfg = Release.DLL|x64
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|x64.Build.0 = Release.DLL|x64
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Release.DLL|x86.Build.0 = Release.DLL|Win32
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|ARM64.ActiveCfg = Release|ARM64
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|ARM64.Build.0 = Release|ARM64
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|x64.ActiveCfg = Release|x64
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|x64.Build.0 = Release|x64
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|x86.ActiveCfg = Release|Win32
+ {1F4722E7-F78E-413F-A106-D3490211EA57}.Release|x86.Build.0 = Release|Win32
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|x64.Build.0 = Debug.DLL|x64
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|ARM64.Build.0 = Debug|ARM64
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|x64.ActiveCfg = Debug|x64
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|x64.Build.0 = Debug|x64
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|x86.ActiveCfg = Debug|Win32
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Debug|x86.Build.0 = Debug|Win32
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|x64.ActiveCfg = Release.DLL|x64
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|x64.Build.0 = Release.DLL|x64
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release.DLL|x86.Build.0 = Release.DLL|Win32
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|ARM64.ActiveCfg = Release|ARM64
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|ARM64.Build.0 = Release|ARM64
+ {0A0FC982-6E31-401F-BA77-3C5E8AB02C68}.Release|x64.ActiveCfg = Release|x64
+ {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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -5194,7 +5532,7 @@ Global
{C54703BF-D68A-480D-BE27-49B62E45D582} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9}
{9CD8BCAD-F212-4BCC-BA98-899743CE3279} = {CC132A4D-D081-4C26-BFB9-AB11984054F8}
{0981CA28-E4A5-4DF1-987F-A41D09131EFC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035}
- {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {278D8859-20B1-428F-8448-064F46E1F021}
+ {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9}
{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}
@@ -5203,9 +5541,9 @@ 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} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C}
- {9DB1F875-6E65-4195-B23F-ED8095C0B99C} = {278D8859-20B1-428F-8448-064F46E1F021}
- {52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F} = {278D8859-20B1-428F-8448-064F46E1F021}
+ {2CCCD9E4-9058-4291-BD89-39C979F0CA1E} = {278D8859-20B1-428F-8448-064F46E1F021}
+ {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}
{A4662163-83E7-4309-8CAA-B0BF13655FE6} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C}
{5F4B766F-DD52-4B53-B6C3-BC7611E17F20} = {278D8859-20B1-428F-8448-064F46E1F021}
@@ -5231,6 +5569,19 @@ Global
{B7FDD40F-DDA4-468E-9C40-EEB175964A26} = {278D8859-20B1-428F-8448-064F46E1F021}
{028F0967-B253-45DA-B1C4-FACCE45D0D8D} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C}
{666346D7-C84B-498D-AE17-53B20C62DB1A} = {278D8859-20B1-428F-8448-064F46E1F021}
+ {AD66AA6A-1E36-4FF0-8670-4F9834BCDB91} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035}
+ {6C897101-BE52-4387-8AA2-062123A76BA1} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035}
+ {4E9D2828-EE83-40C8-97E0-137EEDFBAAAD} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE}
+ {2B3CED91-973F-4936-9DD4-CC8B1C8ACC68} = {CC132A4D-D081-4C26-BFB9-AB11984054F8}
+ {30011884-25EE-42C9-BB15-888CAFB1AA6E} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C}
+ {32FE2658-1D70-442E-8672-0AC5C6F0BD7B} = {278D8859-20B1-428F-8448-064F46E1F021}
+ {842B6472-4AA6-4C2B-A5E5-A62F80DE2C4F} = {278D8859-20B1-428F-8448-064F46E1F021}
+ {FC4DEBD2-4B17-4534-8EEA-BB24A2DBEB5F} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE}
+ {0653AFAF-5578-4C02-AF29-0C873E7634AE} = {278D8859-20B1-428F-8448-064F46E1F021}
+ {071E64F3-1396-4A97-97CA-98CAC059B168} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9}
+ {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}
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 7721a669a..3a7082d77 100644
--- a/projects/VS2022/raylib/raylib.vcxproj
+++ b/projects/VS2022/raylib/raylib.vcxproj
@@ -242,7 +242,7 @@
Level3
Disabled
- _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);GRAPHICS_API_OPENGL_33;PLATFORM_DESKTOP
+ _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32;_DEBUG;_LIB;GRAPHICS_API_OPENGL_33;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(ProjectDir)..\..\..\src\external\glfw\include
@@ -295,7 +295,7 @@
Level3
Disabled
- _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);GRAPHICS_API_OPENGL_33;PLATFORM_DESKTOP;BUILD_LIBTYPE_SHARED
+ _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32;_DEBUG;_LIB;GRAPHICS_API_OPENGL_33;PLATFORM_DESKTOP;BUILD_LIBTYPE_SHARED;%(PreprocessorDefinitions)
CompileAsC
$(ProjectDir)..\..\..\src\external\glfw\include
MultiThreadedDebug
@@ -353,10 +353,11 @@
MaxSpeed
true
true
- _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions);GRAPHICS_API_OPENGL_33;PLATFORM_DESKTOP
+ _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32;NDEBUG;_LIB;GRAPHICS_API_OPENGL_33;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
$(ProjectDir)..\..\..\src\external\glfw\include
CompileAsC
+ AdvancedVectorExtensions2
Windows
@@ -412,7 +413,7 @@
MaxSpeed
true
true
- _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions);GRAPHICS_API_OPENGL_33;PLATFORM_DESKTOP;BUILD_LIBTYPE_SHARED
+ _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32;NDEBUG;_LIB;GRAPHICS_API_OPENGL_33;PLATFORM_DESKTOP;BUILD_LIBTYPE_SHARED;%(PreprocessorDefinitions)
$(ProjectDir)..\..\..\src\external\glfw\include
CompileAsC
MultiThreaded
@@ -532,6 +533,20 @@
true
true
+
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
true
true
diff --git a/projects/VS2022/raylib/raylib.vcxproj.filters b/projects/VS2022/raylib/raylib.vcxproj.filters
index 4588f0521..33030fc9c 100644
--- a/projects/VS2022/raylib/raylib.vcxproj.filters
+++ b/projects/VS2022/raylib/raylib.vcxproj.filters
@@ -49,6 +49,9 @@
Source Files\Platform Files
+
+ Source Files\Platform Files
+
diff --git a/projects/VSCode/Makefile.Android b/projects/VSCode/Makefile.Android
index 7e41ea52f..279790d2a 100644
--- a/projects/VSCode/Makefile.Android
+++ b/projects/VSCode/Makefile.Android
@@ -96,7 +96,7 @@ ifeq ($(ANDROID_ARCH),ARM)
CFLAGS = -std=c99 -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16
endif
ifeq ($(ANDROID_ARCH),ARM64)
- CFLAGS = -std=c99 -target aarch64 -mfix-cortex-a53-835769
+ CFLAGS = -std=c99 -mfix-cortex-a53-835769
endif
# Compilation functions attributes options
CFLAGS += -ffunction-sections -funwind-tables -fstack-protector-strong -fPIC
diff --git a/src/Makefile b/src/Makefile
index 48ff50b30..bc84abece 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -228,7 +228,6 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID)
ifeq ($(ANDROID_ARCH),x86_64)
ANDROID_COMPILER_ARCH = x86_64
endif
-
endif
# Define raylib graphics api depending on selected platform
@@ -261,17 +260,17 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_WIN32)
#GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE)
endif
ifeq ($(TARGET_PLATFORM),PLATFORM_DRM)
- # On DRM OpenGL ES 2.0 must be used
- GRAPHICS = GRAPHICS_API_OPENGL_ES2
+ GRAPHICS ?= GRAPHICS_API_OPENGL_ES2
+ #GRAPHICS = GRAPHICS_API_OPENGL_11_SOFTWARE # Uncomment to use software rendering
endif
ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW))
# On HTML5 OpenGL ES 2.0 is used, emscripten translates it to WebGL 1.0
- GRAPHICS = GRAPHICS_API_OPENGL_ES2
+ GRAPHICS ?= GRAPHICS_API_OPENGL_ES2
#GRAPHICS = GRAPHICS_API_OPENGL_ES3
endif
ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID)
# By default use OpenGL ES 2.0 on Android
- GRAPHICS = GRAPHICS_API_OPENGL_ES2
+ GRAPHICS ?= GRAPHICS_API_OPENGL_ES2
endif
# Define default C compiler and archiver to pack library: CC, AR
@@ -407,7 +406,7 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID)
CFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16
endif
ifeq ($(ANDROID_ARCH),arm64)
- CFLAGS += -target aarch64 -mfix-cortex-a53-835769
+ CFLAGS += -mfix-cortex-a53-835769
endif
ifeq ($(ANDROID_ARCH),x86)
CFLAGS += -march=i686
@@ -636,13 +635,19 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW)
endif
endif
ifeq ($(TARGET_PLATFORM),PLATFORM_DRM)
- LDLIBS = -lGLESv2 -lEGL -ldrm -lgbm -lpthread -lrt -lm -ldl
+ LDLIBS = -ldrm -lgbm -lpthread -lrt -lm -ldl
+ ifeq ($(GRAPHICS),GRAPHICS_API_OPENGL_ES2)
+ LDLIBS += -lGLESv2 -lEGL
+ endif
ifeq ($(RAYLIB_MODULE_AUDIO),TRUE)
LDLIBS += -latomic
endif
endif
ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_WIN32)
- LDLIBS = -lgdi32 -lwinmm -lopengl32 -lshcore
+ LDLIBS = -lgdi32 -lwinmm -lshcore
+ ifneq ($(GRAPHICS),GRAPHICS_API_OPENGL_11_SOFTWARE)
+ LDLIBS += -lopengl32
+ endif
endif
ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID)
LDLIBS = -llog -landroid -lEGL -lGLESv2 -lOpenSLES -lc -lm
diff --git a/src/config.h b/src/config.h
index 43a061fd3..9f54edd23 100644
--- a/src/config.h
+++ b/src/config.h
@@ -49,19 +49,18 @@
#define SUPPORT_RPRAND_GENERATOR 1
// Mouse gestures are directly mapped like touches and processed by gestures system
#define SUPPORT_MOUSE_GESTURES 1
-// Reconfigure standard input to receive key inputs, works with SSH connection.
+// Reconfigure standard input to receive key inputs, works with SSH connection
#define SUPPORT_SSH_KEYBOARD_RPI 1
-// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions.
-// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often.
+// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions
+// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often
#define SUPPORT_WINMM_HIGHRES_TIMER 1
// Use busy wait loop for timing sync, if not defined, a high-resolution timer is set up and used
//#define SUPPORT_BUSY_WAIT_LOOP 1
// Use a partial-busy wait loop, in this case frame sleeps for most of the time, but then runs a busy loop at the end for accuracy
#define SUPPORT_PARTIALBUSY_WAIT_LOOP 1
// Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
+// WARNING: It also requires SUPPORT_IMAGE_EXPORT and SUPPORT_FILEFORMAT_PNG flags
#define SUPPORT_SCREEN_CAPTURE 1
-// Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()
-#define SUPPORT_GIF_RECORDING 1
// Support CompressData() and DecompressData() functions
#define SUPPORT_COMPRESSION_API 1
// Support automatic generated events, loading and recording of those events when required
@@ -76,7 +75,7 @@
#define SUPPORT_CLIPBOARD_IMAGE 1
// NOTE: Clipboard image loading requires support for some image file formats
-// TODO: Those defines should probably be removed from here, I prefer to let the user manage them
+// TODO: Those defines should probably be removed from here, letting the user manage them
#if defined(SUPPORT_CLIPBOARD_IMAGE)
#ifndef SUPPORT_MODULE_RTEXTURES
#define SUPPORT_MODULE_RTEXTURES 1
@@ -226,7 +225,7 @@
// On font atlas image generation [GenImageFontAtlas()], add a 3x3 pixels white rectangle
// at the bottom-right corner of the atlas. It can be useful to for shapes drawing, to allow
-// drawing text and shapes with a single draw call [SetShapesTexture()].
+// drawing text and shapes with a single draw call [SetShapesTexture()]
#define SUPPORT_FONT_ATLAS_WHITE_REC 1
// Support conservative font atlas size estimation
diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h
index 29500f3cf..9c1092695 100644
--- a/src/external/rl_gputex.h
+++ b/src/external/rl_gputex.h
@@ -308,7 +308,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_
unsigned char alpha = 0;
// NOTE: Data comes as A1R5G5B5, it must be reordered to R5G5B5A1
- for (int i = 0; i < image_pixel_size; i++)
+ for (int i = 0; i < data_size/sizeof(unsigned short); i++)
{
alpha = ((unsigned short *)image_data)[i] >> 15;
((unsigned short *)image_data)[i] = ((unsigned short *)image_data)[i] << 1;
@@ -328,7 +328,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_
unsigned char alpha = 0;
// NOTE: Data comes as A4R4G4B4, it must be reordered R4G4B4A4
- for (int i = 0; i < image_pixel_size; i++)
+ for (int i = 0; i < data_size/sizeof(unsigned short); i++)
{
alpha = ((unsigned short *)image_data)[i] >> 12;
((unsigned short *)image_data)[i] = ((unsigned short *)image_data)[i] << 4;
@@ -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/external/rlsw.h b/src/external/rlsw.h
index e049e707e..80fb02c4f 100644
--- a/src/external/rlsw.h
+++ b/src/external/rlsw.h
@@ -7,7 +7,7 @@
* functionality available on rlgl.h library used by raylib, becoming a direct software
* rendering replacement for OpenGL 1.1 backend and allowing to run raylib on GPU-less
* devices when required
-*
+*
* FEATURES:
* - Rendering to custom internal framebuffer with multiple color modes supported:
* - Color buffer: RGB - 8-bit (3:3:2) | RGB - 16-bit (5:6:5) | RGB - 24-bit (8:8:8)
@@ -40,9 +40,17 @@
* 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 RLSW_USE_SIMD_INTRINSICS
+* Detect and use SIMD intrinsics on the host compilation platform
+* SIMD could improve rendering considerable vectorizing some raster operations
+* but the target platforms running the compiled program with SIMD enabled
+* must support the SIMD the program has been built for, making them only
+* recommended under specific situations and only if the developers know
+* what are they doing; this flag is not defined by default
+*
* rlsw capabilities could be customized just defining some internal
* values before library inclusion (default values listed):
-*
+*
* #define SW_GL_FRAMEBUFFER_COPY_BGRA true
* #define SW_GL_BINDING_COPY_TEXTURE true
* #define SW_COLOR_BUFFER_BITS 24
@@ -52,7 +60,7 @@
* #define SW_MAX_TEXTURE_STACK_SIZE 2
* #define SW_MAX_TEXTURES 128
*
-*
+*
* LICENSE: MIT
*
* Copyright (c) 2025-2026 Le Juez Victor (@Bigfoot71), reviewed by Ramon Santamaria (@raysan5)
@@ -63,10 +71,10 @@
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
-*
+*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
-*
+*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -94,11 +102,9 @@
#ifndef SW_MALLOC
#define SW_MALLOC(sz) malloc(sz)
#endif
-
#ifndef SW_REALLOC
#define SW_REALLOC(ptr, newSz) realloc(ptr, newSz)
#endif
-
#ifndef SW_FREE
#define SW_FREE(ptr) free(ptr)
#endif
@@ -116,11 +122,11 @@
#endif
#ifndef SW_COLOR_BUFFER_BITS
- #define SW_COLOR_BUFFER_BITS 32 //< 32 (rgba), 16 (rgb packed) or 8 (rgb packed)
+ #define SW_COLOR_BUFFER_BITS 32 //< 32 (rgba), 16 (rgb packed) or 8 (rgb packed)
#endif
#ifndef SW_DEPTH_BUFFER_BITS
- #define SW_DEPTH_BUFFER_BITS 16 //< 24, 16 or 8
+ #define SW_DEPTH_BUFFER_BITS 16 //< 32, 24 or 16
#endif
#ifndef SW_MAX_PROJECTION_STACK_SIZE
@@ -139,7 +145,7 @@
#define SW_MAX_TEXTURES 128
#endif
-// Under normal circumstances, clipping a polygon can add at most one vertex per clipping plane.
+// Under normal circumstances, clipping a polygon can add at most one vertex per clipping plane
// Considering the largest polygon involved is a quadrilateral (4 vertices),
// and that clipping occurs against both the frustum (6 planes) and the scissors (4 planes),
// the maximum number of vertices after clipping is:
@@ -152,12 +158,6 @@
#define SW_CLIP_EPSILON 1e-4f
#endif
-#ifdef __cplusplus
- #define CURLY_INIT(name) name
-#else
- #define CURLY_INIT(name) (name)
-#endif
-
//----------------------------------------------------------------------------------
// OpenGL Compatibility Types
//----------------------------------------------------------------------------------
@@ -198,6 +198,7 @@ typedef double GLclampd;
//#define GL_ATTRIB_STACK_DEPTH 0x0BB0
//#define GL_CLIENT_ATTRIB_STACK_DEPTH 0x0BB1
#define GL_COLOR_CLEAR_VALUE 0x0C22
+#define GL_DEPTH_CLEAR_VALUE 0x0B73
//#define GL_COLOR_WRITEMASK 0x0C23
//#define GL_CURRENT_INDEX 0x0B01
#define GL_CURRENT_COLOR 0x0B00
@@ -332,6 +333,7 @@ typedef double GLclampd;
#define glViewport(x, y, w, h) swViewport((x), (y), (w), (h))
#define glScissor(x, y, w, h) swScissor((x), (y), (w), (h))
#define glClearColor(r, g, b, a) swClearColor((r), (g), (b), (a))
+#define glClearDepth(d) swClearDepth((d))
#define glClear(bitmask) swClear((bitmask))
#define glBlendFunc(sfactor, dfactor) swBlendFunc((sfactor), (dfactor))
#define glPolygonMode(face, mode) swPolygonMode((mode))
@@ -384,7 +386,6 @@ typedef double GLclampd;
#define glBindTexture(tr, id) swBindTexture((id))
// OpenGL functions NOT IMPLEMENTED by rlsw
-#define glClearDepth(X) ((void)(X))
#define glDepthMask(X) ((void)(X))
#define glColorMask(X,Y,Z,W) ((void)(X),(void)(Y),(void)(Z),(void)(W))
#define glPixelStorei(X,Y) ((void)(X),(void)(Y))
@@ -415,6 +416,7 @@ typedef enum {
SW_VERSION = GL_VERSION,
SW_EXTENSIONS = GL_EXTENSIONS,
SW_COLOR_CLEAR_VALUE = GL_COLOR_CLEAR_VALUE,
+ SW_DEPTH_CLEAR_VALUE = GL_DEPTH_CLEAR_VALUE,
SW_CURRENT_COLOR = GL_CURRENT_COLOR,
SW_CURRENT_TEXTURE_COORDS = GL_CURRENT_TEXTURE_COORDS,
SW_POINT_SIZE = GL_POINT_SIZE,
@@ -529,7 +531,6 @@ SWAPI void swClose(void);
SWAPI bool swResizeFramebuffer(int w, int h);
SWAPI void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type, void *pixels);
SWAPI void swBlitFramebuffer(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc, SWformat format, SWtype type, void *pixels);
-SWAPI void *swGetColorBuffer(int *w, int *h);
SWAPI void swEnable(SWstate state);
SWAPI void swDisable(SWstate state);
@@ -542,6 +543,7 @@ SWAPI void swViewport(int x, int y, int width, int height);
SWAPI void swScissor(int x, int y, int width, int height);
SWAPI void swClearColor(float r, float g, float b, float a);
+SWAPI void swClearDepth(float depth);
SWAPI void swClear(uint32_t bitmask);
SWAPI void swBlendFunc(SWfactor sfactor, SWfactor dfactor);
@@ -608,142 +610,180 @@ SWAPI void swBindTexture(uint32_t id);
#define RLSW_IMPLEMENTATION
#if defined(RLSW_IMPLEMENTATION)
-#include
-#include
-#include // Required for: floorf(), fabsf()
+#include // Required for: malloc(), free()
+#include // Required for: NULL, size_t, uint8_t, uint16_t, uint32_t...
+#include // Required for: sinf(), cosf(), floorf(), fabsf(), sqrtf(), roundf()
-#if defined(__FMA__) && defined(__AVX2__)
-# define SW_HAS_FMA_AVX2
-# include
+// Simple log system to avoid printf() calls if required
+// NOTE: Avoiding those calls, also avoids const strings memory usage
+#define SW_SUPPORT_LOG_INFO
+#if defined(SW_SUPPORT_LOG_INFO) //&& defined(_DEBUG) // WARNING: LOG() output required for this tool
+ #include
+ #define SW_LOG(...) printf(__VA_ARGS__)
+#else
+ #define SW_LOG(...)
#endif
-#if defined(__FMA__) && defined(__AVX__)
-# define SW_HAS_FMA_AVX
-# include
+#if defined(_MSC_VER)
+ #define SW_ALIGN(x) __declspec(align(x))
+#elif defined(__GNUC__) || defined(__clang__)
+ #define SW_ALIGN(x) __attribute__((aligned(x)))
+#else
+ #define SW_ALIGN(x) // Do nothing if not available
#endif
-#if defined(__AVX2__)
-# define SW_HAS_AVX2
-# include
+#if defined(_M_X64) || defined(__x86_64__)
+ #define SW_ARCH_X86_64
+#elif defined(_M_IX86) || defined(__i386__)
+ #define SW_ARCH_X86
+#elif defined(_M_ARM) || defined(__arm__)
+ #define SW_ARCH_ARM32
+#elif defined(_M_ARM64) || defined(__aarch64__)
+ #define SW_ARCH_ARM64
+#elif defined(__riscv)
+ #define SW_ARCH_RISCV
#endif
-#if defined(__AVX__)
-# define SW_HAS_AVX
-# include
-#endif
+#if defined(RLSW_USE_SIMD_INTRINSICS)
+ // Check for SIMD vector instructions
+ // NOTE: Compiler is responsible to enable required flags for host device,
+ // supported features are detected at compiler init but varies depending on compiler
+ // TODO: This logic must be reviewed to avoid the inclusion of multiple headers
+ // and enable the higher level of SIMD available
+ #if defined(__FMA__) && defined(__AVX2__)
+ #define SW_HAS_FMA_AVX2
+ #include
+ #elif defined(__FMA__) && defined(__AVX__)
+ #define SW_HAS_FMA_AVX
+ #include
+ #elif defined(__AVX2__)
+ #define SW_HAS_AVX2
+ #include
+ #elif defined(__AVX__)
+ #define SW_HAS_AVX
+ #include
+ #endif
+ #if defined(__SSE4_2__)
+ #define SW_HAS_SSE42
+ #include
+ #elif defined(__SSE4_1__)
+ #define SW_HAS_SSE41
+ #include
+ #elif defined(__SSSE3__)
+ #define SW_HAS_SSSE3
+ #include
+ #elif defined(__SSE3__)
+ #define SW_HAS_SSE3
+ #include
+ #elif defined(__SSE2__) || (defined(_M_AMD64) || defined(_M_X64)) // SSE2 x64
+ #define SW_HAS_SSE2
+ #include
+ #elif defined(__SSE__)
+ #define SW_HAS_SSE
+ #include
+ #endif
+ #if defined(__ARM_NEON) || defined(__aarch64__)
+ #if defined(__ARM_FEATURE_FMA)
+ #define SW_HAS_NEON_FMA
+ #else
+ #define SW_HAS_NEON
+ #endif
+ #include
+ #endif
+ #if defined(__riscv_vector)
+ // NOTE: Requires compilation flags: -march=rv64gcv -mabi=lp64d
+ #define SW_HAS_RVV
+ #include
+ #endif
+#endif // RLSW_USE_SIMD_INTRINSICS
-#if defined(__SSE4_2__)
-# define SW_HAS_SSE42
-# include
-#endif
-
-#if defined(__SSE4_1__)
-# define SW_HAS_SSE41
-# include
-#endif
-
-#if defined(__SSSE3__)
-# define SW_HAS_SSSE3
-# include
-#endif
-
-#if defined(__SSE3__)
-# define SW_HAS_SSE3
-# include
-#endif
-
-#if defined(__SSE2__)
-# define SW_HAS_SSE2
-# include
-#endif
-
-#if defined(__SSE__)
-# define SW_HAS_SSE
-# include
-#endif
-
-#if defined(__ARM_NEON) || defined(__aarch64__)
-# if defined(__ARM_FEATURE_FMA)
-# define SW_HAS_NEON_FMA
-# else
-# define SW_HAS_NEON
-# endif
-# include
+#ifdef __cplusplus
+ #define SW_CURLY_INIT(name) name
+#else
+ #define SW_CURLY_INIT(name) (name)
#endif
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
#define SW_PI 3.14159265358979323846f
-#define SW_INV_255 0.00392156862745098f
+#define SW_INV_255 0.00392156862745098f // 1.0f/255.0f
#define SW_DEG2RAD (SW_PI/180.0f)
#define SW_RAD2DEG (180.0f/SW_PI)
-#define SW_COLOR_PIXEL_SIZE (SW_COLOR_BUFFER_BITS/8)
-#define SW_DEPTH_PIXEL_SIZE (SW_DEPTH_BUFFER_BITS/8)
+#define SW_COLOR_PIXEL_SIZE (SW_COLOR_BUFFER_BITS >> 3)
+#define SW_DEPTH_PIXEL_SIZE (SW_DEPTH_BUFFER_BITS >> 3)
+#define SW_PIXEL_SIZE (SW_COLOR_PIXEL_SIZE + SW_DEPTH_PIXEL_SIZE)
+
+#if (SW_PIXEL_SIZE <= 4)
+ #define SW_PIXEL_ALIGNMENT 4
+#else // if (SW_PIXEL_SIZE <= 8)
+ #define SW_PIXEL_ALIGNMENT 8
+#endif
#if (SW_COLOR_BUFFER_BITS == 8)
- #define COLOR_TYPE uint8_t
- #define COLOR_IS_PACKED 1
- #define PACK_COLOR(r,g,b) ((((uint8_t)((r)*7+0.5f))&0x07)<<5 | (((uint8_t)((g)*7+0.5f))&0x07)<<2 | ((uint8_t)((b)*3+0.5f))&0x03)
- #define UNPACK_R(p) (((p)>>5)&0x07)
- #define UNPACK_G(p) (((p)>>2)&0x07)
- #define UNPACK_B(p) ((p)&0x03)
- #define SCALE_R(v) ((v)*255+3)/7
- #define SCALE_G(v) ((v)*255+3)/7
- #define SCALE_B(v) ((v)*255+1)/3
- #define TO_FLOAT_R(v) ((v)*(1.0f/7.0f))
- #define TO_FLOAT_G(v) ((v)*(1.0f/7.0f))
- #define TO_FLOAT_B(v) ((v)*(1.0f/3.0f))
+ #define SW_COLOR_TYPE uint8_t
+ #define SW_COLOR_IS_PACKED 1
+ #define SW_COLOR_PACK_COMP 1
+ #define SW_PACK_COLOR(r,g,b) ((((uint8_t)((r)*7+0.5f))&0x07)<<5 | (((uint8_t)((g)*7+0.5f))&0x07)<<2 | ((uint8_t)((b)*3+0.5f))&0x03)
+ #define SW_UNPACK_R(p) (((p)>>5)&0x07)
+ #define SW_UNPACK_G(p) (((p)>>2)&0x07)
+ #define SW_UNPACK_B(p) ((p)&0x03)
+ #define SW_SCALE_R(v) ((v)*255+3)/7
+ #define SW_SCALE_G(v) ((v)*255+3)/7
+ #define SW_SCALE_B(v) ((v)*255+1)/3
+ #define SW_TO_FLOAT_R(v) ((v)*(1.0f/7.0f))
+ #define SW_TO_FLOAT_G(v) ((v)*(1.0f/7.0f))
+ #define SW_TO_FLOAT_B(v) ((v)*(1.0f/3.0f))
#elif (SW_COLOR_BUFFER_BITS == 16)
- #define COLOR_TYPE uint16_t
- #define COLOR_IS_PACKED 1
- #define PACK_COLOR(r,g,b) ((((uint16_t)((r)*31+0.5f))&0x1F)<<11 | (((uint16_t)((g)*63+0.5f))&0x3F)<<5 | ((uint16_t)((b)*31+0.5f))&0x1F)
- #define UNPACK_R(p) (((p)>>11)&0x1F)
- #define UNPACK_G(p) (((p)>>5)&0x3F)
- #define UNPACK_B(p) ((p)&0x1F)
- #define SCALE_R(v) ((v)*255+15)/31
- #define SCALE_G(v) ((v)*255+31)/63
- #define SCALE_B(v) ((v)*255+15)/31
- #define TO_FLOAT_R(v) ((v)*(1.0f/31.0f))
- #define TO_FLOAT_G(v) ((v)*(1.0f/63.0f))
- #define TO_FLOAT_B(v) ((v)*(1.0f/31.0f))
+ #define SW_COLOR_TYPE uint16_t
+ #define SW_COLOR_IS_PACKED 1
+ #define SW_COLOR_PACK_COMP 1
+ #define SW_PACK_COLOR(r,g,b) ((((uint16_t)((r)*31+0.5f))&0x1F)<<11 | (((uint16_t)((g)*63+0.5f))&0x3F)<<5 | ((uint16_t)((b)*31+0.5f))&0x1F)
+ #define SW_UNPACK_R(p) (((p)>>11)&0x1F)
+ #define SW_UNPACK_G(p) (((p)>>5)&0x3F)
+ #define SW_UNPACK_B(p) ((p)&0x1F)
+ #define SW_SCALE_R(v) ((v)*255+15)/31
+ #define SW_SCALE_G(v) ((v)*255+31)/63
+ #define SW_SCALE_B(v) ((v)*255+15)/31
+ #define SW_TO_FLOAT_R(v) ((v)*(1.0f/31.0f))
+ #define SW_TO_FLOAT_G(v) ((v)*(1.0f/63.0f))
+ #define SW_TO_FLOAT_B(v) ((v)*(1.0f/31.0f))
#else // 32 bits
- #define COLOR_TYPE uint8_t
- #define COLOR_IS_PACKED 0
+ #define SW_COLOR_TYPE uint8_t
+ #define SW_COLOR_IS_PACKED 0
+ #define SW_COLOR_PACK_COMP 4
#endif
-#if (SW_DEPTH_BUFFER_BITS == 8)
- #define DEPTH_TYPE uint8_t
- #define DEPTH_IS_PACKED 1
- #define DEPTH_MAX UINT8_MAX
- #define DEPTH_SCALE (1.0f/UINT8_MAX)
- #define PACK_DEPTH(d) ((DEPTH_TYPE)((d)*DEPTH_MAX))
- #define UNPACK_DEPTH(p) (p)
-#elif (SW_DEPTH_BUFFER_BITS == 16)
- #define DEPTH_TYPE uint16_t
- #define DEPTH_IS_PACKED 1
- #define DEPTH_MAX UINT16_MAX
- #define DEPTH_SCALE (1.0f/UINT16_MAX)
- #define PACK_DEPTH(d) ((DEPTH_TYPE)((d)*DEPTH_MAX))
- #define UNPACK_DEPTH(p) (p)
-#else // 24 bits
- #define DEPTH_TYPE uint8_t
- #define DEPTH_IS_PACKED 0
- #define DEPTH_MAX 0xFFFFFF
- #define DEPTH_SCALE (1.0f/0xFFFFFF)
- #define PACK_DEPTH_0(d) (((uint32_t)((d)*DEPTH_MAX)>>16)&0xFF)
- #define PACK_DEPTH_1(d) (((uint32_t)((d)*DEPTH_MAX)>>8)&0xFF)
- #define PACK_DEPTH_2(d) ((uint32_t)((d)*DEPTH_MAX)&0xFF)
- #define UNPACK_DEPTH(p) (((p)[0]<<16)|((p)[1]<<8)|(p)[2])
+#if (SW_DEPTH_BUFFER_BITS == 16)
+ #define SW_DEPTH_TYPE uint16_t
+ #define SW_DEPTH_IS_PACKED 1
+ #define SW_DEPTH_PACK_COMP 1
+ #define SW_DEPTH_MAX UINT16_MAX
+ #define SW_DEPTH_SCALE (1.0f/UINT16_MAX)
+ #define SW_PACK_DEPTH(d) ((SW_DEPTH_TYPE)((d)*SW_DEPTH_MAX))
+ #define SW_UNPACK_DEPTH(p) (p)
+#elif (SW_DEPTH_BUFFER_BITS == 24)
+ #define SW_DEPTH_TYPE uint8_t
+ #define SW_DEPTH_IS_PACKED 0
+ #define SW_DEPTH_PACK_COMP 3
+ #define SW_DEPTH_MAX 0xFFFFFFU
+ #define SW_DEPTH_SCALE (1.0f/0xFFFFFFU)
+ #define SW_PACK_DEPTH_0(d) ((uint8_t)(((uint32_t)((d)*SW_DEPTH_MAX)>>16)&0xFFU))
+ #define SW_PACK_DEPTH_1(d) ((uint8_t)(((uint32_t)((d)*SW_DEPTH_MAX)>>8)&0xFFU))
+ #define SW_PACK_DEPTH_2(d) ((uint8_t)((uint32_t)((d)*SW_DEPTH_MAX)&0xFFU))
+ #define SW_UNPACK_DEPTH(p) ((((uint32_t)(p)[0]<<16)|((uint32_t)(p)[1]<<8)|(uint32_t)(p)[2]))
+#else // 32 bits
+ #define SW_DEPTH_TYPE float
+ #define SW_DEPTH_IS_PACKED 1
+ #define SW_DEPTH_PACK_COMP 1
+ #define SW_DEPTH_MAX 1.0f
+ #define SW_DEPTH_SCALE 1.0f
+ #define SW_PACK_DEPTH(d) ((SW_DEPTH_TYPE)(d))
+ #define SW_UNPACK_DEPTH(p) (p)
#endif
-#define GET_COLOR_PTR(ptr, offset) ((void*)((uint8_t*)(ptr) + (offset)*SW_COLOR_PIXEL_SIZE))
-#define GET_DEPTH_PTR(ptr, offset) ((void*)((uint8_t*)(ptr) + (offset)*SW_DEPTH_PIXEL_SIZE))
-#define INC_COLOR_PTR(ptr) ((ptr) = (void*)((uint8_t*)(ptr) + SW_COLOR_PIXEL_SIZE))
-#define INC_DEPTH_PTR(ptr) ((ptr) = (void*)((uint8_t*)(ptr) + SW_DEPTH_PIXEL_SIZE))
-
-#define SW_STATE_CHECK(flags) (SW_STATE_CHECK_EX(RLSW.stateFlags, (flags)))
+#define SW_STATE_CHECK(flags) (SW_STATE_CHECK_EX(RLSW.stateFlags, (flags)))
#define SW_STATE_CHECK_EX(state, flags) (((state) & (flags)) == (flags))
#define SW_STATE_SCISSOR_TEST (1 << 0)
@@ -808,9 +848,17 @@ typedef struct {
float ty; // Texel height
} sw_texture_t;
+// Pixel data type
+typedef SW_ALIGN(SW_PIXEL_ALIGNMENT) struct {
+ SW_COLOR_TYPE color[SW_COLOR_PACK_COMP];
+ SW_DEPTH_TYPE depth[SW_DEPTH_PACK_COMP];
+#if (SW_PIXEL_SIZE % SW_PIXEL_ALIGNMENT != 0)
+ uint8_t padding[SW_PIXEL_ALIGNMENT - SW_PIXEL_SIZE % SW_PIXEL_ALIGNMENT];
+#endif
+} sw_pixel_t;
+
typedef struct {
- void *color;
- void *depth;
+ sw_pixel_t *pixels;
int width;
int height;
int allocSz;
@@ -818,8 +866,7 @@ typedef struct {
typedef struct {
sw_framebuffer_t framebuffer; // Main framebuffer
- float clearColor[4]; // Color used to clear the screen
- float clearDepth; // Depth value used to clear the screen
+ sw_pixel_t clearValue; // Clear value of the framebuffer
float vpCenter[2]; // Viewport center
float vpHalf[2]; // Viewport half dimensions
@@ -849,7 +896,7 @@ typedef struct {
int vertexCounter; // Number of vertices in 'ctx.vertexBuffer'
SWdraw drawMode; // Current primitive mode (e.g., lines, triangles)
- SWpoly polyMode; // Current polygon filling mode (e.g., lines, triangles)
+ SWpoly polyMode; // Current polygon filling mode (e.g., lines, triangles)
int reqVertices; // Number of vertices required for the primitive being drawn
float pointRadius; // Rasterized point radius
float lineWidth; // Rasterized line width
@@ -1075,38 +1122,54 @@ static inline void sw_float_to_unorm8_simd(uint8_t dst[4], const float src[4])
#if defined(SW_HAS_NEON)
float32x4_t values = vld1q_f32(src);
float32x4_t scaled = vmulq_n_f32(values, 255.0f);
- scaled = vminq_f32(vmaxq_f32(scaled, vdupq_n_f32(0.0f)), vdupq_n_f32(255.0f));
- uint32x4_t clamped = vcvtq_u32_f32(scaled);
-
- uint16x4_t narrow16 = vmovn_u32(clamped);
- uint8x8_t narrow8 = vmovn_u16(vcombine_u16(narrow16, narrow16));
-
- vst1_lane_u32((uint32_t*)dst, vreinterpret_u32_u8(narrow8), 0);
+ int32x4_t clamped_s32 = vcvtq_s32_f32(scaled); // f32 -> s32 (truncated)
+ int16x4_t narrow16_s = vqmovn_s32(clamped_s32);
+ int16x8_t combined16_s = vcombine_s16(narrow16_s, narrow16_s);
+ uint8x8_t narrow8_u = vqmovun_s16(combined16_s);
+ vst1_lane_u32((uint32_t*)dst, vreinterpret_u32_u8(narrow8_u), 0);
#elif defined(SW_HAS_SSE41)
__m128 values = _mm_loadu_ps(src);
__m128 scaled = _mm_mul_ps(values, _mm_set1_ps(255.0f));
- scaled = _mm_max_ps(_mm_min_ps(scaled, _mm_set1_ps(255.0f)), _mm_setzero_ps());
- __m128i clamped = _mm_cvtps_epi32(scaled);
-
- clamped = _mm_packus_epi32(clamped, clamped);
- clamped = _mm_packus_epi16(clamped, clamped);
+ __m128i clamped = _mm_cvtps_epi32(scaled); // f32 -> s32 (truncated)
+ clamped = _mm_packus_epi32(clamped, clamped); // s32 -> u16 (saturated < 0 to 0)
+ clamped = _mm_packus_epi16(clamped, clamped); // u16 -> u8 (saturated > 255 to 255)
*(uint32_t*)dst = _mm_cvtsi128_si32(clamped);
#elif defined(SW_HAS_SSE2)
__m128 values = _mm_loadu_ps(src);
__m128 scaled = _mm_mul_ps(values, _mm_set1_ps(255.0f));
- scaled = _mm_max_ps(_mm_min_ps(scaled, _mm_set1_ps(255.0f)), _mm_setzero_ps());
- __m128i clamped = _mm_cvtps_epi32(scaled);
-
- clamped = _mm_packs_epi32(clamped, clamped);
- clamped = _mm_packus_epi16(clamped, clamped);
+ __m128i clamped = _mm_cvtps_epi32(scaled); // f32 -> s32 (truncated)
+ clamped = _mm_packs_epi32(clamped, clamped); // s32 -> s16 (saturated)
+ clamped = _mm_packus_epi16(clamped, clamped); // s16 -> u8 (saturated < 0 to 0)
*(uint32_t*)dst = _mm_cvtsi128_si32(clamped);
+#elif defined(SW_HAS_RVV)
+ // TODO: Sample code generated by AI, needs testing and review
+ // NOTE: RVV 1.0 specs define the use of __riscv_ prefix for instrinsic functions
+ size_t vl = __riscv_vsetvl_e32m1(4); // Load up to 4 floats into a vector register
+ vfloat32m1_t vsrc = __riscv_vle32_v_f32m1(src, vl); // Load float32 values
+
+ // Clamp to [0.0f, 1.0f]
+ vfloat32m1_t vzero = __riscv_vfmv_v_f_f32m1(0.0f, vl);
+ vfloat32m1_t vone = __riscv_vfmv_v_f_f32m1(1.0f, vl);
+ vsrc = __riscv_vfmin_vv_f32m1(vsrc, vone, vl);
+ vsrc = __riscv_vfmax_vv_f32m1(vsrc, vzero, vl);
+
+ // Multiply by 255.0f and add 0.5f for rounding
+ vfloat32m1_t vscaled = __riscv_vfmul_vf_f32m1(vsrc, 255.0f, vl);
+ vscaled = __riscv_vfadd_vf_f32m1(vscaled, 0.5f, vl);
+
+ // Convert to unsigned integer (truncate toward zero)
+ vuint32m1_t vu32 = __riscv_vfcvt_xu_f_v_u32m1(vscaled, vl);
+
+ // Narrow from u32 -> u8
+ vuint8m1_t vu8 = __riscv_vnclipu_wx_u8m1(vu32, 0, vl); // Round toward zero
+ __riscv_vse8_v_u8m1(dst, vu8, vl); // Store result
#else
for (int i = 0; i < 4; i++)
{
float val = src[i]*255.0f;
val = (val > 255.0f)? 255.0f : val;
val = (val < 0.0f)? 0.0f : val;
- dst[i] = (uint8_t)(val + 0.5f);
+ dst[i] = (uint8_t)val;
}
#endif
}
@@ -1114,29 +1177,33 @@ static inline void sw_float_to_unorm8_simd(uint8_t dst[4], const float src[4])
static inline void sw_float_from_unorm8_simd(float dst[4], const uint8_t src[4])
{
#if defined(SW_HAS_NEON)
- uint32x4_t bytes = vdupq_n_u32(0);
- bytes = vld1q_lane_u32((const uint32_t*)src, bytes, 0);
-
- uint8x8_t bytes8 = vreinterpret_u8_u32(vget_low_u32(bytes));
+ uint8x8_t bytes8 = vld1_u8(src); //< Read 8 bytes, faster, but let's hope we're not at the end of the page (unlikely)...
uint16x8_t bytes16 = vmovl_u8(bytes8);
uint32x4_t ints = vmovl_u16(vget_low_u16(bytes16));
-
float32x4_t floats = vcvtq_f32_u32(ints);
floats = vmulq_n_f32(floats, SW_INV_255);
vst1q_f32(dst, floats);
#elif defined(SW_HAS_SSE41)
- __m128i bytes = _mm_cvtsi32_si128(*(const uint32_t*)src);
+ __m128i bytes = _mm_cvtsi32_si128(*(const uint32_t *)src);
__m128i ints = _mm_cvtepu8_epi32(bytes);
__m128 floats = _mm_cvtepi32_ps(ints);
floats = _mm_mul_ps(floats, _mm_set1_ps(SW_INV_255));
_mm_storeu_ps(dst, floats);
#elif defined(SW_HAS_SSE2)
- __m128i bytes = _mm_cvtsi32_si128(*(const uint32_t*)src);
+ __m128i bytes = _mm_cvtsi32_si128(*(const uint32_t *)src);
bytes = _mm_unpacklo_epi8(bytes, _mm_setzero_si128());
__m128i ints = _mm_unpacklo_epi16(bytes, _mm_setzero_si128());
__m128 floats = _mm_cvtepi32_ps(ints);
floats = _mm_mul_ps(floats, _mm_set1_ps(SW_INV_255));
_mm_storeu_ps(dst, floats);
+#elif defined(SW_HAS_RVV)
+ // TODO: Sample code generated by AI, needs testing and review
+ size_t vl = __riscv_vsetvl_e8m1(4); // Set vector length for 8-bit input elements
+ vuint8m1_t vsrc_u8 = __riscv_vle8_v_u8m1(src, vl); // Load 4 unsigned 8-bit integers
+ vuint32m1_t vsrc_u32 = __riscv_vwcvt_xu_u_v_u32m1(vsrc_u8, vl); // Widen to 32-bit unsigned integers
+ vfloat32m1_t vsrc_f32 = __riscv_vfcvt_f_xu_v_f32m1(vsrc_u32, vl); // Convert to float32
+ vfloat32m1_t vnorm = __riscv_vfmul_vf_f32m1(vsrc_f32, SW_INV_255, vl); // Multiply by 1/255.0 to normalize
+ __riscv_vse32_v_f32m1(dst, vnorm, vl); // Store result
#else
dst[0] = (float)src[0]*SW_INV_255;
dst[1] = (float)src[1]*SW_INV_255;
@@ -1204,15 +1271,8 @@ static inline bool sw_framebuffer_load(int w, int h)
{
int size = w*h;
- RLSW.framebuffer.color = SW_MALLOC(SW_COLOR_PIXEL_SIZE*size);
- if (RLSW.framebuffer.color == NULL) return false;
-
- RLSW.framebuffer.depth = SW_MALLOC(SW_DEPTH_PIXEL_SIZE*size);
- if (RLSW.framebuffer.depth == NULL)
- {
- SW_FREE(RLSW.framebuffer.color);
- return false;
- }
+ RLSW.framebuffer.pixels = SW_MALLOC(sizeof(sw_pixel_t)*size);
+ if (RLSW.framebuffer.pixels == NULL) return false;
RLSW.framebuffer.width = w;
RLSW.framebuffer.height = h;
@@ -1232,18 +1292,10 @@ static inline bool sw_framebuffer_resize(int w, int h)
return true;
}
- void *newColor = SW_REALLOC(RLSW.framebuffer.color, SW_COLOR_PIXEL_SIZE*newSize);
- if (newColor == NULL) return false;
+ void *newPixels = SW_REALLOC(RLSW.framebuffer.pixels, sizeof(sw_pixel_t)*newSize);
+ if (newPixels == NULL) return false;
- void *newDepth = SW_REALLOC(RLSW.framebuffer.depth, SW_DEPTH_PIXEL_SIZE*newSize);
- if (newDepth == NULL)
- {
- SW_FREE(newColor);
- return false;
- }
-
- RLSW.framebuffer.color = newColor;
- RLSW.framebuffer.depth = newDepth;
+ RLSW.framebuffer.pixels = newPixels;
RLSW.framebuffer.width = w;
RLSW.framebuffer.height = h;
@@ -1252,29 +1304,29 @@ static inline bool sw_framebuffer_resize(int w, int h)
return true;
}
-static inline void sw_framebuffer_read_color(float dst[4], const void *src)
+static inline void sw_framebuffer_read_color(float dst[4], const sw_pixel_t *src)
{
-#if COLOR_IS_PACKED
- COLOR_TYPE pixel = ((COLOR_TYPE*)src)[0];
- dst[0] = TO_FLOAT_R(UNPACK_R(pixel));
- dst[1] = TO_FLOAT_G(UNPACK_G(pixel));
- dst[2] = TO_FLOAT_B(UNPACK_B(pixel));
+#if SW_COLOR_IS_PACKED
+ SW_COLOR_TYPE pixel = src->color[0];
+ dst[0] = SW_TO_FLOAT_R(SW_UNPACK_R(pixel));
+ dst[1] = SW_TO_FLOAT_G(SW_UNPACK_G(pixel));
+ dst[2] = SW_TO_FLOAT_B(SW_UNPACK_B(pixel));
dst[3] = 1.0f;
#else
- sw_float_from_unorm8_simd(dst, src);
+ sw_float_from_unorm8_simd(dst, src->color);
#endif
}
-static inline void sw_framebuffer_read_color8(uint8_t dst[4], const void *src)
+static inline void sw_framebuffer_read_color8(uint8_t dst[4], const sw_pixel_t *src)
{
-#if COLOR_IS_PACKED
- COLOR_TYPE pixel = ((COLOR_TYPE*)src)[0];
- dst[0] = SCALE_R(UNPACK_R(pixel));
- dst[1] = SCALE_G(UNPACK_G(pixel));
- dst[2] = SCALE_B(UNPACK_B(pixel));
+#if SW_COLOR_IS_PACKED
+ SW_COLOR_TYPE pixel = src->color[0];
+ dst[0] = SW_SCALE_R(SW_UNPACK_R(pixel));
+ dst[1] = SW_SCALE_G(SW_UNPACK_G(pixel));
+ dst[2] = SW_SCALE_B(SW_UNPACK_B(pixel));
dst[3] = 255;
#else
- const COLOR_TYPE *p = (const COLOR_TYPE*)src;
+ const SW_COLOR_TYPE *p = src->color;
dst[0] = p[0];
dst[1] = p[1];
dst[2] = p[2];
@@ -1282,231 +1334,141 @@ static inline void sw_framebuffer_read_color8(uint8_t dst[4], const void *src)
#endif
}
-static inline void sw_framebuffer_write_color(void *dst, const float src[4])
+static inline float sw_framebuffer_read_depth(const sw_pixel_t *src)
{
-#if COLOR_IS_PACKED
- ((COLOR_TYPE*)dst)[0] = PACK_COLOR(src[0], src[1], src[2]);
+#if SW_DEPTH_IS_PACKED
+ return src->depth[0]*SW_DEPTH_SCALE;
#else
- sw_float_to_unorm8_simd(dst, src);
+ return SW_UNPACK_DEPTH(src->depth)*SW_DEPTH_SCALE;
#endif
}
-static inline void sw_framebuffer_fill_color(void *ptr, int size, const float color[4])
+static inline void sw_framebuffer_write_color(sw_pixel_t *dst, const float src[4])
{
-#if COLOR_IS_PACKED
- COLOR_TYPE packed = PACK_COLOR(color[0], color[1], color[2]);
- COLOR_TYPE *p = (COLOR_TYPE*)ptr;
+#if SW_COLOR_IS_PACKED
+ dst->color[0] = SW_PACK_COLOR(src[0], src[1], src[2]);
#else
- COLOR_TYPE r = sw_clampi(color[0]*255, 0, 255);
- COLOR_TYPE g = sw_clampi(color[1]*255, 0, 255);
- COLOR_TYPE b = sw_clampi(color[2]*255, 0, 255);
- COLOR_TYPE a = sw_clampi(color[3]*255, 0, 255);
- COLOR_TYPE *p = (COLOR_TYPE*)ptr;
+ sw_float_to_unorm8_simd(dst->color, src);
#endif
-
+}
+
+static inline void sw_framebuffer_write_depth(sw_pixel_t *dst, float depth)
+{
+ depth = sw_saturate(depth); // REVIEW: An overflow can occur in certain circumstances with clipping, and needs to be reviewed...
+
+#if SW_DEPTH_IS_PACKED
+ dst->depth[0] = SW_PACK_DEPTH(depth);
+#else
+ dst->depth[0] = SW_PACK_DEPTH_0(depth);
+ dst->depth[1] = SW_PACK_DEPTH_1(depth);
+ dst->depth[2] = SW_PACK_DEPTH_2(depth);
+#endif
+}
+
+static inline void sw_framebuffer_fill_color(sw_pixel_t *ptr, int size, const SW_COLOR_TYPE color[SW_COLOR_PACK_COMP])
+{
if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST)
{
int w = RLSW.scMax[0] - RLSW.scMin[0] + 1;
for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++)
{
-#if COLOR_IS_PACKED
- COLOR_TYPE *row = p + y*RLSW.framebuffer.width + RLSW.scMin[0];
- for (int x = 0; x < w; x++) *row++ = packed;
-#else
- COLOR_TYPE *row = p + 3*(y*RLSW.framebuffer.width + RLSW.scMin[0]);
- for (int x = 0; x < w; x++)
+ sw_pixel_t *row = ptr + y*RLSW.framebuffer.width + RLSW.scMin[0];
+ for (int x = 0; x < w; x++, row++)
{
- *row++ = r;
- *row++ = g;
- *row++ = b;
- *row++ = a;
+ for (int i = 0; i < SW_COLOR_PACK_COMP; i++) row->color[i] = color[i];
}
-#endif
}
}
else
{
-#if COLOR_IS_PACKED
- for (int i = 0; i < size; i++) *p++ = packed;
-#else
- for (int i = 0; i < size; i++)
+ for (int i = 0; i < size; i++, ptr++)
{
- *p++ = r;
- *p++ = g;
- *p++ = b;
- *p++ = a;
+ for (int j = 0; j < SW_COLOR_PACK_COMP; j++) ptr->color[j] = color[j];
}
-#endif
}
}
-static inline float sw_framebuffer_read_depth(const void *src)
+static inline void sw_framebuffer_fill_depth(sw_pixel_t *ptr, int size, const SW_DEPTH_TYPE depth[SW_DEPTH_PACK_COMP])
{
-#if DEPTH_IS_PACKED
- return ((DEPTH_TYPE*)src)[0]*DEPTH_SCALE;
-#else
- const DEPTH_TYPE *p = (const DEPTH_TYPE*)src;
- uint32_t d = UNPACK_DEPTH(p);
- return d*DEPTH_SCALE;
-#endif
-}
-
-static inline void sw_framebuffer_write_depth(void *dst, float depth)
-{
-#if DEPTH_IS_PACKED
- ((DEPTH_TYPE*)dst)[0] = PACK_DEPTH(depth);
-#else
- DEPTH_TYPE *p = (DEPTH_TYPE*)dst;
- p[0] = PACK_DEPTH_0(depth);
- p[1] = PACK_DEPTH_1(depth);
- p[2] = PACK_DEPTH_2(depth);
-#endif
-}
-
-static inline void sw_framebuffer_fill_depth(void *ptr, int size, float value)
-{
-#if DEPTH_IS_PACKED
- DEPTH_TYPE d = PACK_DEPTH(value);
- DEPTH_TYPE *p = (DEPTH_TYPE*)ptr;
-#else
- DEPTH_TYPE d0 = PACK_DEPTH_0(value);
- DEPTH_TYPE d1 = PACK_DEPTH_1(value);
- DEPTH_TYPE d2 = PACK_DEPTH_2(value);
- DEPTH_TYPE *p = (DEPTH_TYPE*)ptr;
-#endif
-
if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST)
{
int w = RLSW.scMax[0] - RLSW.scMin[0] + 1;
for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++)
{
-#if DEPTH_IS_PACKED
- DEPTH_TYPE *row = p + y*RLSW.framebuffer.width + RLSW.scMin[0];
- for (int x = 0; x < w; x++) *row++ = d;
-#else
- DEPTH_TYPE *row = p + 3*(y*RLSW.framebuffer.width + RLSW.scMin[0]);
- for (int x = 0; x < w; x++)
+ sw_pixel_t *row = ptr + y*RLSW.framebuffer.width + RLSW.scMin[0];
+ for (int x = 0; x < w; x++, row++)
{
- *row++ = d0;
- *row++ = d1;
- *row++ = d2;
+ for (int i = 0; i < SW_DEPTH_PACK_COMP; i++) row->depth[i] = depth[i];
}
-#endif
}
}
else
{
-#if DEPTH_IS_PACKED
- for (int i = 0; i < size; i++) *p++ = d;
-#else
- for (int i = 0; i < size; i++)
+ for (int i = 0; i < size; i++, ptr++)
{
- *p++ = d0;
- *p++ = d1;
- *p++ = d2;
+ for (int j = 0; j < SW_DEPTH_PACK_COMP; j++) ptr->depth[j] = depth[j];
}
-#endif
}
}
-static inline void sw_framebuffer_fill(void *colorPtr, void *depthPtr, int size, float color[4], float depth)
+static inline void sw_framebuffer_fill(sw_pixel_t *ptr, int size, sw_pixel_t value)
{
-#if COLOR_IS_PACKED
- COLOR_TYPE packedColor = PACK_COLOR(color[0], color[1], color[2]);
- COLOR_TYPE *pColor = (COLOR_TYPE*)colorPtr;
-#else
- COLOR_TYPE r = sw_clampi(color[0]*255, 0, 255);
- COLOR_TYPE g = sw_clampi(color[1]*255, 0, 255);
- COLOR_TYPE b = sw_clampi(color[2]*255, 0, 255);
- COLOR_TYPE a = sw_clampi(color[3]*255, 0, 255);
- COLOR_TYPE *pColor = (COLOR_TYPE*)colorPtr;
-#endif
-
-#if DEPTH_IS_PACKED
- DEPTH_TYPE d = PACK_DEPTH(depth);
- DEPTH_TYPE *pDepth = (DEPTH_TYPE*)depthPtr;
-#else
- DEPTH_TYPE d0 = PACK_DEPTH_0(depth);
- DEPTH_TYPE d1 = PACK_DEPTH_1(depth);
- DEPTH_TYPE d2 = PACK_DEPTH_2(depth);
- DEPTH_TYPE *pDepth = (DEPTH_TYPE*)depthPtr;
-#endif
-
if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST)
{
int w = RLSW.scMax[0] - RLSW.scMin[0] + 1;
for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++)
{
-#if COLOR_IS_PACKED
- COLOR_TYPE *rowColor = pColor + y*RLSW.framebuffer.width + RLSW.scMin[0];
-#else
- COLOR_TYPE *rowColor = pColor + 3*(y*RLSW.framebuffer.width + RLSW.scMin[0]);
-#endif
-
-#if DEPTH_IS_PACKED
- DEPTH_TYPE *rowDepth = pDepth + y*RLSW.framebuffer.width + RLSW.scMin[0];
-#else
- DEPTH_TYPE *rowDepth = pDepth + 3*(y*RLSW.framebuffer.width + RLSW.scMin[0]);
-#endif
-
- for (int x = 0; x < w; x++)
- {
-#if COLOR_IS_PACKED
- *rowColor++ = packedColor;
-#else
- *rowColor++ = r;
- *rowColor++ = g;
- *rowColor++ = b;
- *rowColor++ = a;
-#endif
-
-#if DEPTH_IS_PACKED
- *rowDepth++ = d;
-#else
- *rowDepth++ = d0;
- *rowDepth++ = d1;
- *rowDepth++ = d2;
-#endif
- }
+ sw_pixel_t *row = ptr + y*RLSW.framebuffer.width + RLSW.scMin[0];
+ for (int x = 0; x < w; x++, row++) *row = value;
}
}
else
{
+ for (int i = 0; i < size; i++, ptr++) *ptr = value;
+ }
+}
+
+static inline void sw_framebuffer_copy_fast(void* dst)
+{
+ int size = RLSW.framebuffer.width*RLSW.framebuffer.height;
+ const sw_pixel_t *pixels = RLSW.framebuffer.pixels;
+
+#if SW_COLOR_BUFFER_BITS == 8
+ uint8_t *dst8 = (uint8_t*)dst;
+ for (int i = 0; i < size; i++) dst8[i] = pixels[i].color[0];
+#elif SW_COLOR_BUFFER_BITS == 16
+ uint16_t *dst16 = (uint16_t*)dst;
+ for (int i = 0; i < size; i++) dst16[i] = *(uint16_t*)pixels[i].color;
+#else // 32 bits
+ uint32_t *dst32 = (uint32_t*)dst;
+ #if SW_GL_FRAMEBUFFER_COPY_BGRA
for (int i = 0; i < size; i++)
{
-#if COLOR_IS_PACKED
- *pColor++ = packedColor;
-#else
- *pColor++ = r;
- *pColor++ = g;
- *pColor++ = b;
- *pColor++ = a;
-#endif
-
-#if DEPTH_IS_PACKED
- *pDepth++ = d;
-#else
- *pDepth++ = d0;
- *pDepth++ = d1;
- *pDepth++ = d2;
-#endif
+ const uint8_t *c = pixels[i].color;
+ dst32[i] = (uint32_t)c[2] | ((uint32_t)c[1] << 8) | ((uint32_t)c[0] << 16) | ((uint32_t)c[3] << 24);
}
- }
+ #else // RGBA
+ for (int i = 0; i < size; i++) dst32[i] = *(uint32_t*)pixels[i].color;
+ #endif
+#endif
}
#define DEFINE_FRAMEBUFFER_COPY_BEGIN(name, DST_PTR_T) \
-static inline void sw_framebuffer_copy_to_##name(int x, int y, int w, int h, DST_PTR_T *dst) \
+static inline void sw_framebuffer_copy_to_##name(int x, int y, int w, int h, DST_PTR_T *dst) \
{ \
- const void *src = RLSW.framebuffer.color; \
+ const int stride = RLSW.framebuffer.width; \
+ const sw_pixel_t *src = RLSW.framebuffer.pixels + (y*stride + x); \
\
- for (int iy = y; iy < h; iy++) { \
- for (int ix = x; ix < w; ix++) { \
+ for (int iy = 0; iy < h; iy++) { \
+ const sw_pixel_t *line = src; \
+ for (int ix = 0; ix < w; ix++) { \
uint8_t color[4]; \
- sw_framebuffer_read_color8(color, src); \
+ sw_framebuffer_read_color8(color, line); \
#define DEFINE_FRAMEBUFFER_COPY_END() \
- INC_COLOR_PTR(src); \
+ ++line; \
} \
+ src += stride; \
} \
}
@@ -1568,7 +1530,7 @@ DEFINE_FRAMEBUFFER_COPY_BEGIN(R5G5B5A1, uint16_t)
uint8_t r5 = (color[0]*31 + 127)/255;
uint8_t g5 = (color[1]*31 + 127)/255;
uint8_t b5 = (color[2]*31 + 127)/255;
- uint8_t a1 = color[3] >= 128 ? 1 : 0;
+ uint8_t a1 = (color[3] >= 128)? 1 : 0;
#if SW_GL_FRAMEBUFFER_COPY_BGRA
uint16_t pixel = (b5 << 11) | (g5 << 6) | (r5 << 1) | a1;
@@ -1620,22 +1582,24 @@ static inline void sw_framebuffer_blit_to_##name(
int xSrc, int ySrc, int wSrc, int hSrc, \
DST_PTR_T *dst) \
{ \
- const uint8_t *srcBase = (uint8_t*)RLSW.framebuffer.color; \
- int fbWidth = RLSW.framebuffer.width; \
+ const sw_pixel_t *srcBase = RLSW.framebuffer.pixels; \
+ const int fbWidth = RLSW.framebuffer.width; \
\
- uint32_t xScale = ((uint32_t)wSrc << 16)/(uint32_t)wDst; \
- uint32_t yScale = ((uint32_t)hSrc << 16)/(uint32_t)hDst; \
+ const uint32_t xScale = ((uint32_t)wSrc << 16)/(uint32_t)wDst; \
+ const uint32_t yScale = ((uint32_t)hSrc << 16)/(uint32_t)hDst; \
\
for (int dy = 0; dy < hDst; dy++) { \
uint32_t yFix = ((uint32_t)ySrc << 16) + dy*yScale; \
int sy = yFix >> 16; \
+ const sw_pixel_t *srcLine = srcBase + sy*fbWidth + xSrc; \
\
+ const sw_pixel_t *srcPtr = srcLine; \
for (int dx = 0; dx < wDst; dx++) { \
uint32_t xFix = dx*xScale; \
int sx = xFix >> 16; \
- const void *srcPtr = GET_COLOR_PTR(srcBase, sy*fbWidth + sx); \
+ const sw_pixel_t *pixel = srcPtr + sx; \
uint8_t color[4]; \
- sw_framebuffer_read_color8(color, srcPtr); \
+ sw_framebuffer_read_color8(color, pixel);
#define DEFINE_FRAMEBUFFER_BLIT_END() \
} \
@@ -1697,7 +1661,7 @@ DEFINE_FRAMEBUFFER_BLIT_BEGIN(R5G5B5A1, uint16_t)
uint8_t r5 = (color[0]*31 + 127)/255;
uint8_t g5 = (color[1]*31 + 127)/255;
uint8_t b5 = (color[2]*31 + 127)/255;
- uint8_t a1 = color[3] >= 128 ? 1 : 0;
+ uint8_t a1 = (color[3] >= 128)? 1 : 0;
#if SW_GL_FRAMEBUFFER_COPY_BGRA
uint16_t pixel = (b5 << 11) | (g5 << 6) | (r5 << 1) | a1;
@@ -1955,7 +1919,7 @@ static inline void sw_texture_sample_nearest(float *color, const sw_texture_t *t
static inline void sw_texture_sample_linear(float *color, const sw_texture_t *tex, float u, float v)
{
// TODO: With a bit more cleverness we could clearly reduce the
- // number of operations here, but for now it works fine.
+ // number of operations here, but for now it works fine
float xf = (u*tex->width) - 0.5f;
float yf = (v*tex->height) - 0.5f;
@@ -2154,12 +2118,12 @@ static inline int sw_clip_##name(
// Frustum cliping functions
//-------------------------------------------------------------------------------------------
#define IS_INSIDE_PLANE_W(h) ((h)[3] >= SW_CLIP_EPSILON)
-#define IS_INSIDE_PLANE_X_POS(h) ((h)[0] <= (h)[3])
-#define IS_INSIDE_PLANE_X_NEG(h) (-(h)[0] <= (h)[3])
-#define IS_INSIDE_PLANE_Y_POS(h) ((h)[1] <= (h)[3])
-#define IS_INSIDE_PLANE_Y_NEG(h) (-(h)[1] <= (h)[3])
-#define IS_INSIDE_PLANE_Z_POS(h) ((h)[2] <= (h)[3])
-#define IS_INSIDE_PLANE_Z_NEG(h) (-(h)[2] <= (h)[3])
+#define IS_INSIDE_PLANE_X_POS(h) ( (h)[0] < (h)[3]) // Exclusive for +X
+#define IS_INSIDE_PLANE_X_NEG(h) (-(h)[0] < (h)[3]) // Exclusive for -X
+#define IS_INSIDE_PLANE_Y_POS(h) ( (h)[1] < (h)[3]) // Exclusive for +Y
+#define IS_INSIDE_PLANE_Y_NEG(h) (-(h)[1] < (h)[3]) // Exclusive for -Y
+#define IS_INSIDE_PLANE_Z_POS(h) ( (h)[2] <= (h)[3]) // Inclusive for +Z
+#define IS_INSIDE_PLANE_Z_NEG(h) (-(h)[2] <= (h)[3]) // Inclusive for -Z
#define COMPUTE_T_PLANE_W(hPrev, hCurr) ((SW_CLIP_EPSILON - (hPrev)[3])/((hCurr)[3] - (hPrev)[3]))
#define COMPUTE_T_PLANE_X_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[0])/(((hPrev)[3] - (hPrev)[0]) - ((hCurr)[3] - (hCurr)[0])))
@@ -2239,13 +2203,13 @@ static inline bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VE
//-------------------------------------------------------------------------------------------
static inline bool sw_triangle_face_culling(void)
{
- // NOTE: Face culling is done before clipping to avoid unnecessary computations.
+ // NOTE: Face culling is done before clipping to avoid unnecessary computations
// To handle triangles crossing the w=0 plane correctly,
// we perform the winding order test in homogeneous coordinates directly,
- // before the perspective division (division by w).
+ // before the perspective division (division by w)
// This test determines the orientation of the triangle in the (x,y,w) plane,
// which corresponds to the projected 2D winding order sign,
- // even with negative w values.
+ // even with negative w values
// Preload homogeneous coordinates into local variables
const float *h0 = RLSW.vertexBuffer[0].homogeneous;
@@ -2253,11 +2217,11 @@ static inline bool sw_triangle_face_culling(void)
const float *h2 = RLSW.vertexBuffer[2].homogeneous;
// Compute a value proportional to the signed area in the projected 2D plane,
- // calculated directly using homogeneous coordinates BEFORE division by w.
+ // calculated directly using homogeneous coordinates BEFORE division by w
// This is the determinant of the matrix formed by the (x, y, w) components
// of the vertices, which correctly captures the winding order in homogeneous
// space and its relationship to the projected 2D winding order, even with
- // negative w values.
+ // negative w values
// The determinant formula used here is:
// h0.x*(h1.y*h2.w - h2.y*h1.w) +
// h1.x*(h2.y*h0.w - h0.y*h2.w) +
@@ -2269,20 +2233,18 @@ static inline bool sw_triangle_face_culling(void)
h2[0]*(h0[1]*h1[3] - h1[1]*h0[3]);
// Discard the triangle if its winding order (determined by the sign
- // of the homogeneous area/determinant) matches the culled direction.
+ // of the homogeneous area/determinant) matches the culled direction
// A positive hSgnArea typically corresponds to a counter-clockwise
- // winding in the projected space when all w > 0.
+ // winding in the projected space when all w > 0
// This test is robust for points with w > 0 or w < 0, correctly
- // capturing the change in orientation when crossing the w=0 plane.
+ // capturing the change in orientation when crossing the w=0 plane
- // The culling logic remains the same based on the signed area/determinant.
+ // The culling logic remains the same based on the signed area/determinant
// A value of 0 for hSgnArea means the points are collinear in (x, y, w)
- // space, which corresponds to a degenerate triangle projection.
+ // space, which corresponds to a degenerate triangle projection
// Such triangles are typically not culled by this test (0 < 0 is false, 0 > 0 is false)
- // and should be handled by the clipper if necessary.
- return (RLSW.cullFace == SW_FRONT)
- ? (hSgnArea < 0) // Cull if winding is "clockwise" in the projected sense
- : (hSgnArea > 0); // Cull if winding is "counter-clockwise" in the projected sense
+ // and should be handled by the clipper if necessary
+ return (RLSW.cullFace == SW_FRONT)? (hSgnArea < 0) : (hSgnArea > 0); // Cull if winding is "clockwise" : "counter-clockwise"
}
static inline void sw_triangle_clip_and_project(void)
@@ -2378,8 +2340,7 @@ static inline void FUNC_NAME(const sw_texture_t *tex, const sw_vertex_t *start,
\
/* Pre-calculate the starting pointers for the framebuffer row */ \
int y = (int)start->screen[1]; \
- void *cptr = GET_COLOR_PTR(RLSW.framebuffer.color, y*RLSW.framebuffer.width + xStart); \
- void *dptr = GET_DEPTH_PTR(RLSW.framebuffer.depth, y*RLSW.framebuffer.width + xStart); \
+ sw_pixel_t *ptr = RLSW.framebuffer.pixels + y*RLSW.framebuffer.width + xStart; \
\
/* Scanline rasterization */ \
for (int x = xStart; x < xEnd; x++) \
@@ -2395,12 +2356,12 @@ static inline void FUNC_NAME(const sw_texture_t *tex, const sw_vertex_t *start,
if (ENABLE_DEPTH_TEST) \
{ \
/* TODO: Implement different depth funcs? */ \
- float depth = sw_framebuffer_read_depth(dptr); \
+ float depth = sw_framebuffer_read_depth(ptr); \
if (z > depth) goto discard; \
} \
\
/* TODO: Implement depth mask */ \
- sw_framebuffer_write_depth(dptr, z); \
+ sw_framebuffer_write_depth(ptr, z); \
\
if (ENABLE_TEXTURE) \
{ \
@@ -2417,13 +2378,13 @@ static inline void FUNC_NAME(const sw_texture_t *tex, const sw_vertex_t *start,
if (ENABLE_COLOR_BLEND) \
{ \
float dstColor[4]; \
- sw_framebuffer_read_color(dstColor, cptr); \
+ sw_framebuffer_read_color(dstColor, ptr); \
sw_blend_colors(dstColor, srcColor); \
- sw_framebuffer_write_color(cptr, dstColor); \
+ sw_framebuffer_write_color(ptr, dstColor); \
} \
else \
{ \
- sw_framebuffer_write_color(cptr, srcColor); \
+ sw_framebuffer_write_color(ptr, srcColor); \
} \
\
/* Increment the interpolation parameter, UVs, and pointers */ \
@@ -2439,9 +2400,7 @@ static inline void FUNC_NAME(const sw_texture_t *tex, const sw_vertex_t *start,
u += dUdx; \
v += dVdx; \
} \
- \
- INC_COLOR_PTR(cptr); \
- INC_DEPTH_PTR(dptr); \
+ ++ptr; \
} \
}
@@ -2598,14 +2557,14 @@ static inline void sw_triangle_render(void)
//-------------------------------------------------------------------------------------------
static inline bool sw_quad_face_culling(void)
{
- // NOTE: Face culling is done before clipping to avoid unnecessary computations.
+ // NOTE: Face culling is done before clipping to avoid unnecessary computations
// To handle quads crossing the w=0 plane correctly,
// we perform the winding order test in homogeneous coordinates directly,
- // before the perspective division (division by w).
+ // before the perspective division (division by w)
// For a convex quad with vertices P0, P1, P2, P3 in sequential order,
// the winding order of the quad is the same as the winding order
// of the triangle P0 P1 P2. We use the homogeneous triangle
- // winding test on this first triangle.
+ // winding test on this first triangle
// Preload homogeneous coordinates into local variables
const float *h0 = RLSW.vertexBuffer[0].homogeneous;
@@ -2617,11 +2576,11 @@ static inline bool sw_quad_face_culling(void)
// Compute a value proportional to the signed area of the triangle P0 P1 P2
// in the projected 2D plane, calculated directly using homogeneous coordinates
- // BEFORE division by w.
+ // BEFORE division by w
// This is the determinant of the matrix formed by the (x, y, w) components
// of the vertices P0, P1, and P2. Its sign correctly indicates the winding order
// in homogeneous space and its relationship to the projected 2D winding order,
- // even with negative w values.
+ // even with negative w values
// The determinant formula used here is:
// h0.x*(h1.y*h2.w - h2.y*h1.w) +
// h1.x*(h2.y*h0.w - h0.y*h2.w) +
@@ -2633,21 +2592,19 @@ static inline bool sw_quad_face_culling(void)
h2[0]*(h0[1]*h1[3] - h1[1]*h0[3]);
// Perform face culling based on the winding order determined by the sign
- // of the homogeneous area/determinant of triangle P0 P1 P2.
+ // of the homogeneous area/determinant of triangle P0 P1 P2
// This test is robust for points with w > 0 or w < 0 within the triangle,
- // correctly capturing the change in orientation when crossing the w=0 plane.
+ // correctly capturing the change in orientation when crossing the w=0 plane
// A positive hSgnArea typically corresponds to a counter-clockwise
- // winding in the projected space when all w > 0.
+ // winding in the projected space when all w > 0
// A value of 0 for hSgnArea means P0, P1, P2 are collinear in (x, y, w)
- // space, which corresponds to a degenerate triangle projection.
+ // space, which corresponds to a degenerate triangle projection
// Such quads might also be degenerate or non-planar. They are typically
// not culled by this test (0 < 0 is false, 0 > 0 is false)
- // and should be handled by the clipper if necessary.
+ // and should be handled by the clipper if necessary
- return (RLSW.cullFace == SW_FRONT)
- ? (hSgnArea < 0.0f) // Cull if winding is "clockwise" in the projected sense
- : (hSgnArea > 0.0f); // Cull if winding is "counter-clockwise" in the projected sense
+ return (RLSW.cullFace == SW_FRONT)? (hSgnArea < 0.0f) : (hSgnArea > 0.0f); // Cull if winding is "clockwise" : "counter-clockwise"
}
static inline void sw_quad_clip_and_project(void)
@@ -2690,25 +2647,38 @@ static inline void sw_quad_clip_and_project(void)
static inline bool sw_quad_is_axis_aligned(void)
{
- int horizontal = 0;
- int vertical = 0;
-
+ // Reject quads with perspective projection
+ // The fast path assumes affine (non-perspective) quads,
+ // so we require all vertices to have homogeneous w = 1.0
for (int i = 0; i < 4; i++)
{
if (RLSW.vertexBuffer[i].homogeneous[3] != 1.0f) return false;
-
- const float *v0 = RLSW.vertexBuffer[i].position;
- const float *v1 = RLSW.vertexBuffer[(i + 1)%4].position;
-
- float dx = v1[0] - v0[0];
- float dy = v1[1] - v0[1];
-
- if ((fabsf(dx) > 1e-6f) && (fabsf(dy) < 1e-6f)) horizontal++;
- else if ((fabsf(dy) > 1e-6f) && (fabsf(dx) < 1e-6f)) vertical++;
- else return false; // Diagonal edge -> not axis-aligned
}
- return ((horizontal == 2) && (vertical == 2));
+ // Epsilon tolerance in screen space (pixels)
+ const float epsilon = 0.5f;
+
+ // Fetch screen-space positions for the four quad vertices
+ const float *p0 = RLSW.vertexBuffer[0].screen;
+ const float *p1 = RLSW.vertexBuffer[1].screen;
+ const float *p2 = RLSW.vertexBuffer[2].screen;
+ const float *p3 = RLSW.vertexBuffer[3].screen;
+
+ // Compute edge vectors between consecutive vertices
+ // These define the four sides of the quad in screen space
+ float dx01 = p1[0] - p0[0], dy01 = p1[1] - p0[1];
+ float dx12 = p2[0] - p1[0], dy12 = p2[1] - p1[1];
+ float dx23 = p3[0] - p2[0], dy23 = p3[1] - p2[1];
+ float dx30 = p0[0] - p3[0], dy30 = p0[1] - p3[1];
+
+ // Each edge must be either horizontal or vertical within epsilon tolerance
+ // If any edge deviates significantly from either axis, the quad is not axis-aligned
+ if (!((fabsf(dy01) < epsilon) || (fabsf(dx01) < epsilon))) return false;
+ if (!((fabsf(dy12) < epsilon) || (fabsf(dx12) < epsilon))) return false;
+ if (!((fabsf(dy23) < epsilon) || (fabsf(dx23) < epsilon))) return false;
+ if (!((fabsf(dy30) < epsilon) || (fabsf(dx30) < epsilon))) return false;
+
+ return true;
}
static inline void sw_quad_sort_cw(const sw_vertex_t* *output)
@@ -2716,9 +2686,9 @@ static inline void sw_quad_sort_cw(const sw_vertex_t* *output)
const sw_vertex_t *input = RLSW.vertexBuffer;
// Calculate the centroid of the quad
- float cx = (input[0].screen[0] + input[1].screen[0] +
+ float cx = (input[0].screen[0] + input[1].screen[0] +
input[2].screen[0] + input[3].screen[0])*0.25f;
- float cy = (input[0].screen[1] + input[1].screen[1] +
+ float cy = (input[0].screen[1] + input[1].screen[1] +
input[2].screen[1] + input[3].screen[1])*0.25f;
// Calculate the angle of each vertex relative to the center
@@ -2782,8 +2752,8 @@ static inline void FUNC_NAME(void)
float ySubstep = 1.0f - sw_fract(v0->screen[1]); \
\
/* Calculation of vertex gradients in X and Y */ \
- float dUdx, dVdx; \
- float dUdy, dVdy; \
+ float dUdx = 0.0f, dVdx = 0.0f; \
+ float dUdy = 0.0f, dVdy = 0.0f; \
if (ENABLE_TEXTURE) { \
dUdx = (v1->texcoord[0] - v0->texcoord[0])*wRcp; \
dVdx = (v1->texcoord[1] - v0->texcoord[1])*wRcp; \
@@ -2809,8 +2779,7 @@ static inline void FUNC_NAME(void)
const sw_texture_t *tex; \
if (ENABLE_TEXTURE) tex = &RLSW.loadedTextures[RLSW.currentTexture]; \
\
- void *cDstBase = RLSW.framebuffer.color; \
- void *dDstBase = RLSW.framebuffer.depth; \
+ sw_pixel_t *pixels = RLSW.framebuffer.pixels; \
int wDst = RLSW.framebuffer.width; \
\
float zScanline = v0->homogeneous[2] + dZdx*xSubstep + dZdy*ySubstep; \
@@ -2826,8 +2795,7 @@ static inline void FUNC_NAME(void)
\
for (int y = yMin; y < yMax; y++) \
{ \
- void *cptr = GET_COLOR_PTR(cDstBase, y*wDst + xMin); \
- void *dptr = GET_DEPTH_PTR(dDstBase, y*wDst + xMin); \
+ sw_pixel_t *ptr = pixels + y*wDst + xMin; \
\
float z = zScanline; \
float u = uScanline; \
@@ -2855,12 +2823,12 @@ static inline void FUNC_NAME(void)
if (ENABLE_DEPTH_TEST) \
{ \
/* TODO: Implement different depth funcs? */ \
- float depth = sw_framebuffer_read_depth(dptr); \
+ float depth = sw_framebuffer_read_depth(ptr); \
if (z > depth) goto discard; \
} \
\
/* TODO: Implement depth mask */ \
- sw_framebuffer_write_depth(dptr, z); \
+ sw_framebuffer_write_depth(ptr, z); \
\
if (ENABLE_TEXTURE) \
{ \
@@ -2875,11 +2843,11 @@ static inline void FUNC_NAME(void)
if (ENABLE_COLOR_BLEND) \
{ \
float dstColor[4]; \
- sw_framebuffer_read_color(dstColor, cptr); \
+ sw_framebuffer_read_color(dstColor, ptr); \
sw_blend_colors(dstColor, srcColor); \
- sw_framebuffer_write_color(cptr, dstColor); \
+ sw_framebuffer_write_color(ptr, dstColor); \
} \
- else sw_framebuffer_write_color(cptr, srcColor); \
+ else sw_framebuffer_write_color(ptr, srcColor); \
\
discard: \
z += dZdx; \
@@ -2887,15 +2855,12 @@ static inline void FUNC_NAME(void)
color[1] += dCdx[1]; \
color[2] += dCdx[2]; \
color[3] += dCdx[3]; \
- \
if (ENABLE_TEXTURE) \
{ \
u += dUdx; \
v += dVdx; \
} \
- \
- INC_COLOR_PTR(cptr); \
- INC_DEPTH_PTR(dptr); \
+ ++ptr; \
} \
\
zScanline += dZdy; \
@@ -3119,8 +3084,7 @@ static inline void FUNC_NAME(const sw_vertex_t *v0, const sw_vertex_t *v1) \
float a = v0->color[3] + aInc*substep; \
\
const int fbWidth = RLSW.framebuffer.width; \
- void *cBuffer = RLSW.framebuffer.color; \
- void *dBuffer = RLSW.framebuffer.depth; \
+ sw_pixel_t *pixels = RLSW.framebuffer.pixels; \
\
int numPixels = (int)(steps - substep) + 1; \
\
@@ -3130,28 +3094,26 @@ static inline void FUNC_NAME(const sw_vertex_t *v0, const sw_vertex_t *v1) \
int px = (int)(x - 0.5f); \
int py = (int)(y - 0.5f); \
\
- int offset = py*fbWidth + px; \
- void *dptr = GET_DEPTH_PTR(dBuffer, offset); \
+ sw_pixel_t *ptr = pixels + py*fbWidth + px; \
\
if (ENABLE_DEPTH_TEST) \
{ \
- float depth = sw_framebuffer_read_depth(dptr); \
+ float depth = sw_framebuffer_read_depth(ptr); \
if (z > depth) goto discard; \
} \
\
- sw_framebuffer_write_depth(dptr, z); \
+ sw_framebuffer_write_depth(ptr, z); \
\
- void *cptr = GET_COLOR_PTR(cBuffer, offset); \
float color[4] = {r, g, b, a}; \
\
if (ENABLE_COLOR_BLEND) \
{ \
float dstColor[4]; \
- sw_framebuffer_read_color(dstColor, cptr); \
+ sw_framebuffer_read_color(dstColor, ptr); \
sw_blend_colors(dstColor, color); \
- sw_framebuffer_write_color(cptr, dstColor); \
+ sw_framebuffer_write_color(ptr, dstColor); \
} \
- else sw_framebuffer_write_color(cptr, color); \
+ else sw_framebuffer_write_color(ptr, color); \
\
discard: \
x += xInc; y += yInc; z += zInc; \
@@ -3292,27 +3254,24 @@ static inline void FUNC_NAME(int x, int y, float z, const float color[4]) \
} \
\
int offset = y*RLSW.framebuffer.width + x; \
- \
- void *dptr = GET_DEPTH_PTR(RLSW.framebuffer.depth, offset); \
+ sw_pixel_t *ptr = RLSW.framebuffer.pixels + offset; \
\
if (ENABLE_DEPTH_TEST) \
{ \
- float depth = sw_framebuffer_read_depth(dptr); \
+ float depth = sw_framebuffer_read_depth(ptr); \
if (z > depth) return; \
} \
\
- sw_framebuffer_write_depth(dptr, z); \
- \
- void *cptr = GET_COLOR_PTR(RLSW.framebuffer.color, offset); \
+ sw_framebuffer_write_depth(ptr, z); \
\
if (ENABLE_COLOR_BLEND) \
{ \
float dstColor[4]; \
- sw_framebuffer_read_color(dstColor, cptr); \
+ sw_framebuffer_read_color(dstColor, ptr); \
sw_blend_colors(dstColor, color); \
- sw_framebuffer_write_color(cptr, dstColor); \
+ sw_framebuffer_write_color(ptr, dstColor); \
} \
- else sw_framebuffer_write_color(cptr, color); \
+ else sw_framebuffer_write_color(ptr, color); \
}
#define DEFINE_POINT_THICK_RASTER(FUNC_NAME, RASTER_FUNC) \
@@ -3598,11 +3557,9 @@ bool swInit(int w, int h)
RLSW.freeTextureIds = (uint32_t *)SW_MALLOC(SW_MAX_TEXTURES*sizeof(uint32_t));
if (RLSW.loadedTextures == NULL) { swClose(); return false; }
- RLSW.clearColor[0] = 0.0f;
- RLSW.clearColor[1] = 0.0f;
- RLSW.clearColor[2] = 0.0f;
- RLSW.clearColor[3] = 1.0f;
- RLSW.clearDepth = 1.0f;
+ const float clearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
+ sw_framebuffer_write_color(&RLSW.clearValue, clearColor);
+ sw_framebuffer_write_depth(&RLSW.clearValue, 1.0f);
RLSW.currentMatrixMode = SW_MODELVIEW;
RLSW.currentMatrix = &RLSW.stackModelview[0];
@@ -3655,6 +3612,23 @@ bool swInit(int w, int h)
RLSW.loadedTextureCount = 1;
+ SW_LOG("INFO: RLSW: Software renderer initialized successfully\n");
+#if defined(SW_HAS_FMA_AVX) && defined(SW_HAS_FMA_AVX2)
+ SW_LOG("INFO: RLSW: Using SIMD instructions: FMA AVX\n");
+#endif
+#if defined(SW_HAS_AVX) || defined(SW_HAS_AVX2)
+ SW_LOG("INFO: RLSW: Using SIMD instructions: AVX\n");
+#endif
+#if defined(SW_HAS_SSE) || defined(SW_HAS_SSE2) || defined(SW_HAS_SSE3) || defined(SW_HAS_SSSE3) || defined(SW_HAS_SSE41) || defined(SW_HAS_SSE42)
+ SW_LOG("INFO: RLSW: Using SIMD instructions: SSE\n");
+#endif
+#if defined(SW_HAS_NEON_FMA) || defined(SW_HAS_NEON)
+ SW_LOG("INFO: RLSW: Using SIMD instructions: NEON\n");
+#endif
+#if defined(SW_HAS_RVV)
+ SW_LOG("INFO: RLSW: Using SIMD instructions: RVV\n");
+#endif
+
return true;
}
@@ -3669,12 +3643,11 @@ void swClose(void)
}
}
- SW_FREE(RLSW.framebuffer.color);
- SW_FREE(RLSW.framebuffer.depth);
+ SW_FREE(RLSW.framebuffer.pixels);
SW_FREE(RLSW.loadedTextures);
SW_FREE(RLSW.freeTextureIds);
- RLSW = CURLY_INIT(sw_context_t) { 0 };
+ RLSW = SW_CURLY_INIT(sw_context_t) { 0 };
}
bool swResizeFramebuffer(int w, int h)
@@ -3686,17 +3659,8 @@ void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type,
{
sw_pixelformat_t pFormat = (sw_pixelformat_t)sw_get_pixel_format(format, type);
- if (w <= 0)
- {
- RLSW.errCode = SW_INVALID_VALUE;
- return;
- }
-
- if (h <= 0)
- {
- RLSW.errCode = SW_INVALID_VALUE;
- return;
- }
+ if (w <= 0) { RLSW.errCode = SW_INVALID_VALUE; return; }
+ if (h <= 0) { RLSW.errCode = SW_INVALID_VALUE; return; }
if (w > RLSW.framebuffer.width) w = RLSW.framebuffer.width;
if (h > RLSW.framebuffer.height) h = RLSW.framebuffer.height;
@@ -3704,6 +3668,25 @@ void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type,
x = sw_clampi(x, 0, w);
y = sw_clampi(y, 0, h);
+ if ((x >= w) || (y >= h)) return;
+
+ if ((x == 0) && (y == 0) && (w == RLSW.framebuffer.width) && (h == RLSW.framebuffer.height))
+ {
+ #if SW_COLOR_BUFFER_BITS == 32
+ if (pFormat == SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
+ {
+ sw_framebuffer_copy_fast(pixels);
+ return;
+ }
+ #elif SW_COLOR_BUFFER_BITS == 16
+ if (pFormat == SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5)
+ {
+ sw_framebuffer_copy_fast(pixels);
+ return;
+ }
+ #endif
+ }
+
switch (pFormat)
{
case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: sw_framebuffer_copy_to_GRAYALPHA(x, y, w, h, (uint8_t *)pixels); break;
@@ -3712,7 +3695,7 @@ void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type,
case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: sw_framebuffer_copy_to_R8G8B8(x, y, w, h, (uint8_t *)pixels); break;
case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: sw_framebuffer_copy_to_R5G5B5A1(x, y, w, h, (uint16_t *)pixels); break;
case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: sw_framebuffer_copy_to_R4G4B4A4(x, y, w, h, (uint16_t *)pixels); break;
- case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: sw_framebuffer_copy_to_R8G8B8A8(x, y, w, h, (uint8_t *)pixels); break;
+ //case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: sw_framebuffer_copy_to_R8G8B8A8(x, y, w, h, (uint8_t *)pixels); break;
// Below: not implemented
case SW_PIXELFORMAT_UNCOMPRESSED_R32:
case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32:
@@ -3720,9 +3703,7 @@ void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type,
case SW_PIXELFORMAT_UNCOMPRESSED_R16:
case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16:
case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:
- default:
- RLSW.errCode = SW_INVALID_ENUM;
- break;
+ default: RLSW.errCode = SW_INVALID_ENUM; break;
}
}
@@ -3730,17 +3711,8 @@ void swBlitFramebuffer(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySr
{
sw_pixelformat_t pFormat = (sw_pixelformat_t)sw_get_pixel_format(format, type);
- if (wSrc <= 0)
- {
- RLSW.errCode = SW_INVALID_VALUE;
- return;
- }
-
- if (hSrc <= 0)
- {
- RLSW.errCode = SW_INVALID_VALUE;
- return;
- }
+ if (wSrc <= 0) { RLSW.errCode = SW_INVALID_VALUE; return; }
+ if (hSrc <= 0) { RLSW.errCode = SW_INVALID_VALUE; return; }
if (wSrc > RLSW.framebuffer.width) wSrc = RLSW.framebuffer.width;
if (hSrc > RLSW.framebuffer.height) hSrc = RLSW.framebuffer.height;
@@ -3748,6 +3720,13 @@ void swBlitFramebuffer(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySr
xSrc = sw_clampi(xSrc, 0, wSrc);
ySrc = sw_clampi(ySrc, 0, hSrc);
+ // Check if the sizes are identical after clamping the source to avoid unexpected issues
+ // REVIEW: This repeats the operations if true, so we could make a copy function without these checks
+ if (xDst == xSrc && yDst == ySrc && wDst == wSrc && hDst == hSrc)
+ {
+ swCopyFramebuffer(xSrc, ySrc, wSrc, hSrc, format, type, pixels);
+ }
+
switch (pFormat)
{
case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: sw_framebuffer_blit_to_GRAYALPHA(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, (uint8_t *)pixels); break;
@@ -3770,14 +3749,6 @@ void swBlitFramebuffer(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySr
}
}
-void *swGetColorBuffer(int *w, int *h)
-{
- if (w) *w = RLSW.framebuffer.width;
- if (h) *h = RLSW.framebuffer.height;
-
- return RLSW.framebuffer.color;
-}
-
void swEnable(SWstate state)
{
switch (state)
@@ -3821,10 +3792,11 @@ void swGetFloatv(SWget name, float *v)
{
case SW_COLOR_CLEAR_VALUE:
{
- v[0] = RLSW.clearColor[0];
- v[1] = RLSW.clearColor[1];
- v[2] = RLSW.clearColor[2];
- v[3] = RLSW.clearColor[3];
+ sw_framebuffer_read_color(v, &RLSW.clearValue);
+ } break;
+ case SW_DEPTH_CLEAR_VALUE:
+ {
+ v[0] = sw_framebuffer_read_depth(&RLSW.clearValue);
} break;
case SW_CURRENT_COLOR:
{
@@ -3932,10 +3904,13 @@ void swScissor(int x, int y, int width, int height)
void swClearColor(float r, float g, float b, float a)
{
- RLSW.clearColor[0] = r;
- RLSW.clearColor[1] = g;
- RLSW.clearColor[2] = b;
- RLSW.clearColor[3] = a;
+ float v[4] = { r, g, b, a };
+ sw_framebuffer_write_color(&RLSW.clearValue, v);
+}
+
+void swClearDepth(float depth)
+{
+ sw_framebuffer_write_depth(&RLSW.clearValue, depth);
}
void swClear(uint32_t bitmask)
@@ -3944,15 +3919,15 @@ void swClear(uint32_t bitmask)
if ((bitmask & (SW_COLOR_BUFFER_BIT | SW_DEPTH_BUFFER_BIT)) == (SW_COLOR_BUFFER_BIT | SW_DEPTH_BUFFER_BIT))
{
- sw_framebuffer_fill(RLSW.framebuffer.color, RLSW.framebuffer.depth,size, RLSW.clearColor, RLSW.clearDepth);
+ sw_framebuffer_fill(RLSW.framebuffer.pixels, size, RLSW.clearValue);
}
else if (bitmask & (SW_COLOR_BUFFER_BIT))
{
- sw_framebuffer_fill_color(RLSW.framebuffer.color, size, RLSW.clearColor);
+ sw_framebuffer_fill_color(RLSW.framebuffer.pixels, size, RLSW.clearValue.color);
}
else if (bitmask & SW_DEPTH_BUFFER_BIT)
{
- sw_framebuffer_fill_depth(RLSW.framebuffer.depth, size, RLSW.clearDepth);
+ sw_framebuffer_fill_depth(RLSW.framebuffer.pixels, size, RLSW.clearValue.depth);
}
}
@@ -4353,7 +4328,7 @@ void swVertex2f(float x, float y)
void swVertex2fv(const float *v)
{
const float v4[4] = { v[0], v[1], 0.0f, 1.0f };
- sw_immediate_push_vertex(v, RLSW.current.color, RLSW.current.texcoord);
+ sw_immediate_push_vertex(v4, RLSW.current.color, RLSW.current.texcoord);
}
void swVertex3i(int x, int y, int z)
@@ -4371,7 +4346,7 @@ void swVertex3f(float x, float y, float z)
void swVertex3fv(const float *v)
{
const float v4[4] = { v[0], v[1], v[2], 1.0f };
- sw_immediate_push_vertex(v, RLSW.current.color, RLSW.current.texcoord);
+ sw_immediate_push_vertex(v4, RLSW.current.color, RLSW.current.texcoord);
}
void swVertex4i(int x, int y, int z, int w)
@@ -4513,19 +4488,19 @@ void swDrawArrays(SWdraw mode, int offset, int count)
const float *texMatrix = RLSW.stackTexture[RLSW.stackTextureCounter - 1];
const float *defaultTexcoord = RLSW.current.texcoord;
const float *defaultColor = RLSW.current.color;
-
+
const float *positions = RLSW.array.positions;
const float *texcoords = RLSW.array.texcoords;
const uint8_t *colors = RLSW.array.colors;
int end = offset + count;
-
+
for (int i = offset; i < end; i++)
{
float u, v;
if (texcoords)
{
- int idx = 2 * i;
+ int idx = 2*i;
u = texcoords[idx];
v = texcoords[idx + 1];
}
@@ -4536,8 +4511,8 @@ void swDrawArrays(SWdraw mode, int offset, int count)
}
float texcoord[2];
- texcoord[0] = texMatrix[0] * u + texMatrix[4] * v + texMatrix[12];
- texcoord[1] = texMatrix[1] * u + texMatrix[5] * v + texMatrix[13];
+ texcoord[0] = texMatrix[0]*u + texMatrix[4]*v + texMatrix[12];
+ texcoord[1] = texMatrix[1]*u + texMatrix[5]*v + texMatrix[13];
float color[4] = {
defaultColor[0],
@@ -4548,14 +4523,14 @@ void swDrawArrays(SWdraw mode, int offset, int count)
if (colors)
{
- int idx = 4 * i;
+ int idx = 4*i;
color[0] *= (float)colors[idx]*SW_INV_255;
color[1] *= (float)colors[idx + 1]*SW_INV_255;
color[2] *= (float)colors[idx + 2]*SW_INV_255;
color[3] *= (float)colors[idx + 3]*SW_INV_255;
}
- int idx = 3 * i;
+ int idx = 3*i;
float position[4] = {
positions[idx],
positions[idx + 1],
@@ -4608,20 +4583,20 @@ void swDrawElements(SWdraw mode, int count, int type, const void *indices)
const float *texMatrix = RLSW.stackTexture[RLSW.stackTextureCounter - 1];
const float *defaultTexcoord = RLSW.current.texcoord;
const float *defaultColor = RLSW.current.color;
-
+
const float *positions = RLSW.array.positions;
const float *texcoords = RLSW.array.texcoords;
const uint8_t *colors = RLSW.array.colors;
-
+
for (int i = 0; i < count; i++)
{
- int index = indicesUb ? indicesUb[i] :
- (indicesUs ? indicesUs[i] : indicesUi[i]);
-
+ int index = indicesUb? indicesUb[i] :
+ (indicesUs? indicesUs[i] : indicesUi[i]);
+
float u, v;
if (texcoords)
{
- int idx = 2 * index;
+ int idx = 2*index;
u = texcoords[idx];
v = texcoords[idx + 1];
}
@@ -4632,8 +4607,8 @@ void swDrawElements(SWdraw mode, int count, int type, const void *indices)
}
float texcoord[2];
- texcoord[0] = texMatrix[0] * u + texMatrix[4] * v + texMatrix[12];
- texcoord[1] = texMatrix[1] * u + texMatrix[5] * v + texMatrix[13];
+ texcoord[0] = texMatrix[0]*u + texMatrix[4]*v + texMatrix[12];
+ texcoord[1] = texMatrix[1]*u + texMatrix[5]*v + texMatrix[13];
float color[4] = {
defaultColor[0],
@@ -4644,14 +4619,14 @@ void swDrawElements(SWdraw mode, int count, int type, const void *indices)
if (colors)
{
- int idx = 4 * index;
+ int idx = 4*index;
color[0] *= (float)colors[idx]*SW_INV_255;
color[1] *= (float)colors[idx + 1]*SW_INV_255;
color[2] *= (float)colors[idx + 2]*SW_INV_255;
color[3] *= (float)colors[idx + 3]*SW_INV_255;
}
- int idx = 3 * index;
+ int idx = 3*index;
float position[4] = {
positions[idx],
positions[idx + 1],
diff --git a/src/external/sdefl.h b/src/external/sdefl.h
index 36015b95b..bdc45b7eb 100644
--- a/src/external/sdefl.h
+++ b/src/external/sdefl.h
@@ -198,7 +198,7 @@ extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl);
static int
sdefl_ilog2(int n) {
if (!n) return 0;
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && !defined(__llvm__) && !defined(__INTEL_COMPILER) // @raysan5, address PR #5367
unsigned long msbp = 0;
_BitScanReverse(&msbp, (unsigned long)n);
return (int)msbp;
diff --git a/src/external/sinfl.h b/src/external/sinfl.h
index a749501ca..c8d0f96d0 100644
--- a/src/external/sinfl.h
+++ b/src/external/sinfl.h
@@ -171,7 +171,7 @@ extern int zsinflate(void *out, int cap, const void *in, int size);
static int
sinfl_bsr(unsigned n) {
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && !defined(__llvm__) && !defined(__INTEL_COMPILER) // @raysan5, address PR #5367
unsigned long uln = 0;
_BitScanReverse(&uln, n);
return (int)(uln);
diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c
index 47dcce32b..f150bf638 100644
--- a/src/platforms/rcore_android.c
+++ b/src/platforms/rcore_android.c
@@ -84,7 +84,6 @@ typedef struct {
// Global Variables Definition
//----------------------------------------------------------------------------------
extern CoreData CORE; // Global CORE state context
-extern bool isGpuReady; // Flag to note GPU has been initialized successfully
static PlatformData platform = { 0 }; // Platform specific data
//----------------------------------------------------------------------------------
@@ -268,6 +267,8 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd);
static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs
static GamepadButton AndroidTranslateGamepadButton(int button); // Map Android gamepad button to raylib gamepad button
+static void SetupFramebuffer(int width, int height); // Setup main framebuffer (required by InitPlatform())
+
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
@@ -358,13 +359,17 @@ void RestoreWindow(void)
// Set window configuration state using flags
void SetWindowState(unsigned int flags)
{
- TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform");
+ 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);
}
// Clear window configuration state flags
void ClearWindowState(unsigned int flags)
{
- TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform");
+ // State change: FLAG_WINDOW_ALWAYS_RUN
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN);
}
// Set icon for window
@@ -602,7 +607,7 @@ void DisableCursor(void)
// Swap back buffer with front buffer (screen drawing)
void SwapScreenBuffer(void)
{
- eglSwapBuffers(platform.device, platform.surface);
+ if (platform.surface != EGL_NO_SURFACE) eglSwapBuffers(platform.device, platform.surface);
}
//----------------------------------------------------------------------------------
@@ -623,10 +628,9 @@ double GetTime(void)
}
// 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.
-// Ref: https://github.com/raysan5/raylib/issues/686
+// 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
@@ -687,7 +691,7 @@ void SetMouseCursor(int cursor)
TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
}
-// Get physical key name.
+// Get physical key name
const char *GetKeyName(int key)
{
TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
@@ -742,15 +746,15 @@ void PollInputEvents(void)
int pollEvents = 0;
// Poll Events (registered events) until we reach TIMEOUT which indicates there are no events left to poll
- // NOTE: Activity is paused if not enabled (platform.appEnabled)
- while ((pollResult = ALooper_pollOnce(platform.appEnabled? 0 : -1, NULL, &pollEvents, ((void **)&platform.source)) > ALOOPER_POLL_TIMEOUT))
+ // NOTE: Activity is paused if not enabled (platform.appEnabled) and always run flag is not set (FLAG_WINDOW_ALWAYS_RUN)
+ while ((pollResult = ALooper_pollOnce((platform.appEnabled || FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN))? 0 : -1, NULL, &pollEvents, ((void **)&platform.source)) > ALOOPER_POLL_TIMEOUT))
{
// Process this event
if (platform.source != NULL) platform.source->process(platform.app, platform.source);
- // NOTE: Allow closing the window in case a configuration change happened.
+ // NOTE: Allow closing the window in case a configuration change happened
// The android_main function should be allowed to return to its caller in order for the
- // Android OS to relaunch the activity.
+ // Android OS to relaunch the activity
if (platform.app->destroyRequested != 0)
{
CORE.Window.shouldClose = true;
@@ -796,10 +800,10 @@ int InitPlatform(void)
//AConfiguration_getScreenLong(platform.app->config);
// Set some default window flags
- CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false
- CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false
- CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true
- CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_HIDDEN); // false
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // false
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); // true
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // false
//----------------------------------------------------------------------------
// Initialize App command system
@@ -829,13 +833,13 @@ int InitPlatform(void)
// Wait for window to be initialized (display and context)
while (!CORE.Window.ready)
{
- // Process events until we reach TIMEOUT, which indicates no more events queued.
+ // Process events until we reach TIMEOUT, which indicates no more events queued
while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, ((void **)&platform.source)) > ALOOPER_POLL_TIMEOUT))
{
// Process this event
if (platform.source != NULL) platform.source->process(platform.app, platform.source);
- // NOTE: It's highly likely destroyRequested will never be non-zero at the start of the activity lifecycle.
+ // NOTE: It's highly likely destroyRequested will never be non-zero at the start of the activity lifecycle
//if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true;
}
}
@@ -869,8 +873,9 @@ void ClosePlatform(void)
platform.device = EGL_NO_DISPLAY;
}
- // NOTE: Reset global state in case the activity is being relaunched.
- if (platform.app->destroyRequested != 0) {
+ // NOTE: Reset global state in case the activity is being relaunched
+ if (platform.app->destroyRequested != 0)
+ {
CORE = (CoreData){0};
platform = (PlatformData){0};
}
@@ -882,12 +887,11 @@ void ClosePlatform(void)
// NOTE: returns false in case graphic device could not be created
static int InitGraphicsDevice(void)
{
- CORE.Window.fullscreen = true;
- CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
+ FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
EGLint samples = 0;
EGLint sampleBuffer = 0;
- if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
{
samples = 4;
sampleBuffer = 1;
@@ -925,7 +929,7 @@ static int InitGraphicsDevice(void)
// Initialize the EGL device connection
if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE)
{
- // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
+ // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred
TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
return -1;
}
@@ -992,7 +996,7 @@ static int InitGraphicsDevice(void)
CORE.Window.ready = true;
- if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow();
return 0;
}
@@ -1042,7 +1046,6 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
// Initialize OpenGL context (states and resources)
// NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
- isGpuReady = true;
// Setup default viewport
// NOTE: It updated CORE.Window.render.width and CORE.Window.render.height
@@ -1059,7 +1062,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
// Set font white rectangle for shapes drawing, so shapes and text can be batched together
// WARNING: rshapes module is required, if not available, default internal white rectangle is used
Rectangle rec = GetFontDefault().recs[95];
- if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
{
// NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering
SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 });
@@ -1081,41 +1084,26 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
// Initialize random seed
SetRandomSeed((unsigned int)time(NULL));
-
- // TODO: GPU assets reload in case of lost focus (lost context)
- // NOTE: This problem has been solved just unbinding and rebinding context from display
- /*
- if (assetsReloadRequired)
- {
- for (int i = 0; i < assetCount; i++)
- {
- // TODO: Unload old asset if required
-
- // Load texture again to pointed texture
- (*textureAsset + i) = LoadTexture(assetPath[i]);
- }
- }
- */
}
}
} break;
case APP_CMD_GAINED_FOCUS:
{
platform.appEnabled = true;
- CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
//ResumeMusicStream();
} break;
case APP_CMD_PAUSE: break;
case APP_CMD_LOST_FOCUS:
{
platform.appEnabled = false;
- CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
//PauseMusicStream();
} break;
case APP_CMD_TERM_WINDOW:
{
// Detach OpenGL context and destroy display surface
- // NOTE 1: This case is used when the user exits the app without closing it. We detach the context to ensure everything is recoverable upon resuming.
+ // NOTE 1: This case is used when the user exits the app without closing it, context is detached to ensure everything is recoverable upon resuming
// NOTE 2: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...)
// NOTE 3: In some cases (too many context loaded), OS could unload context automatically... :(
if (platform.device != EGL_NO_DISPLAY)
@@ -1179,16 +1167,16 @@ static GamepadButton AndroidTranslateGamepadButton(int button)
static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
{
// If additional inputs are required check:
- // https://developer.android.com/ndk/reference/group/input
- // https://developer.android.com/training/game-controllers/controller-input
+ // REF: https://developer.android.com/ndk/reference/group/input
+ // REF: https://developer.android.com/training/game-controllers/controller-input
int type = AInputEvent_getType(event);
int source = AInputEvent_getSource(event);
if (type == AINPUT_EVENT_TYPE_MOTION)
{
- if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) ||
- ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD))
+ if (FLAG_IS_SET(source, AINPUT_SOURCE_JOYSTICK) ||
+ FLAG_IS_SET(source, AINPUT_SOURCE_GAMEPAD))
{
// For now we'll assume a single gamepad which we "detect" on its input event
CORE.Input.Gamepad.ready[0] = true;
@@ -1251,8 +1239,11 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
//int32_t AKeyEvent_getMetaState(event);
// Handle gamepad button presses and releases
- if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) ||
- ((source & AINPUT_SOURCE_GAMEPAD) == 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;
@@ -1290,7 +1281,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
{
// Let the OS handle input to avoid app stuck. Behaviour: CMD_PAUSE -> CMD_SAVE_STATE -> CMD_STOP -> CMD_CONFIG_CHANGED -> CMD_LOST_FOCUS
// Resuming Behaviour: CMD_START -> CMD_RESUME -> CMD_CONFIG_CHANGED -> CMD_CONFIG_CHANGED -> CMD_GAINED_FOCUS
- // It seems like locking mobile, screen size (CMD_CONFIG_CHANGED) is affected.
+ // It seems like locking mobile, screen size (CMD_CONFIG_CHANGED) is affected
// NOTE: AndroidManifest.xml must have
// Before that change, activity was calling CMD_TERM_WINDOW and CMD_DESTROY when locking mobile, so that was not a normal behaviour
return 0;
@@ -1419,15 +1410,9 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1;
else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0;
- // Stores the previous position of touch[0] only while it's active to calculate the delta.
- if (flags == AMOTION_EVENT_ACTION_MOVE)
- {
- CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
- }
- else
- {
- CORE.Input.Mouse.previousPosition = CORE.Input.Touch.position[0];
- }
+ // Stores the previous position of touch[0] only while it's active to calculate the delta
+ if (flags == AMOTION_EVENT_ACTION_MOVE) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
+ else CORE.Input.Mouse.previousPosition = CORE.Input.Touch.position[0];
// Map touch[0] as mouse input for convenience
CORE.Input.Mouse.currentPosition = CORE.Input.Touch.position[0];
@@ -1436,4 +1421,82 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
return 0;
}
+// Compute framebuffer size relative to screen size and display size
+// NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified
+static void SetupFramebuffer(int width, int height)
+{
+ // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var)
+ if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height))
+ {
+ TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
+
+ // Downscaling to fit display with border-bars
+ float widthRatio = (float)CORE.Window.display.width/(float)CORE.Window.screen.width;
+ float heightRatio = (float)CORE.Window.display.height/(float)CORE.Window.screen.height;
+
+ if (widthRatio <= heightRatio)
+ {
+ CORE.Window.render.width = CORE.Window.display.width;
+ CORE.Window.render.height = (int)round((float)CORE.Window.screen.height*widthRatio);
+ CORE.Window.renderOffset.x = 0;
+ CORE.Window.renderOffset.y = (CORE.Window.display.height - CORE.Window.render.height);
+ }
+ else
+ {
+ CORE.Window.render.width = (int)round((float)CORE.Window.screen.width*heightRatio);
+ CORE.Window.render.height = CORE.Window.display.height;
+ CORE.Window.renderOffset.x = (CORE.Window.display.width - CORE.Window.render.width);
+ CORE.Window.renderOffset.y = 0;
+ }
+
+ // Screen scaling required
+ float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width;
+ CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f);
+
+ // NOTE: We render to full display resolution!
+ // We just need to calculate above parameters for downscale matrix and offsets
+ CORE.Window.render.width = CORE.Window.display.width;
+ CORE.Window.render.height = CORE.Window.display.height;
+
+ TRACELOG(LOG_WARNING, "DISPLAY: Downscale matrix generated, content will be rendered at (%ix%i)", CORE.Window.render.width, CORE.Window.render.height);
+ }
+ else if ((CORE.Window.screen.width < CORE.Window.display.width) || (CORE.Window.screen.height < CORE.Window.display.height))
+ {
+ // Required screen size is smaller than display size
+ TRACELOG(LOG_INFO, "DISPLAY: Upscaling required: Screen size (%ix%i) smaller than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
+
+ if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0))
+ {
+ CORE.Window.screen.width = CORE.Window.display.width;
+ CORE.Window.screen.height = CORE.Window.display.height;
+ }
+
+ // Upscaling to fit display with border-bars
+ float displayRatio = (float)CORE.Window.display.width/(float)CORE.Window.display.height;
+ float screenRatio = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
+
+ if (displayRatio <= screenRatio)
+ {
+ CORE.Window.render.width = CORE.Window.screen.width;
+ CORE.Window.render.height = (int)round((float)CORE.Window.screen.width/displayRatio);
+ CORE.Window.renderOffset.x = 0;
+ CORE.Window.renderOffset.y = (CORE.Window.render.height - CORE.Window.screen.height);
+ }
+ else
+ {
+ CORE.Window.render.width = (int)round((float)CORE.Window.screen.height*displayRatio);
+ CORE.Window.render.height = CORE.Window.screen.height;
+ CORE.Window.renderOffset.x = (CORE.Window.render.width - CORE.Window.screen.width);
+ CORE.Window.renderOffset.y = 0;
+ }
+ }
+ else
+ {
+ CORE.Window.render.width = CORE.Window.screen.width;
+ CORE.Window.render.height = CORE.Window.screen.height;
+ CORE.Window.renderOffset.x = 0;
+ CORE.Window.renderOffset.y = 0;
+ }
+}
+
// EOF
diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c
index 5dde1df67..9b360771f 100644
--- a/src/platforms/rcore_desktop_glfw.c
+++ b/src/platforms/rcore_desktop_glfw.c
@@ -78,28 +78,27 @@
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#include // Required for: timespec, nanosleep(), select() - POSIX
-#if defined(_GLFW_X11) || defined(_GLFW_WAYLAND)
+ #if defined(_GLFW_X11) || defined(_GLFW_WAYLAND)
// Set appropriate expose macros based on available backends
- #if defined(_GLFW_X11)
- #define GLFW_EXPOSE_NATIVE_X11
- #define Font X11Font // Hack to fix 'Font' name collision
+ #if defined(_GLFW_X11)
+ #define GLFW_EXPOSE_NATIVE_X11
+ #define Font X11Font // Hack to fix 'Font' name collision
// The definition and references to the X11 Font type will be replaced by 'X11Font'
// Works as long as the current file consistently references any X11 Font as X11Font
// Since it is never referenced (as of writing), this does not pose an issue
- #endif
+ #endif
- #if defined(_GLFW_WAYLAND)
- #define GLFW_EXPOSE_NATIVE_WAYLAND
- #endif
+ #if defined(_GLFW_WAYLAND)
+ #define GLFW_EXPOSE_NATIVE_WAYLAND
+ #endif
- #include "GLFW/glfw3native.h" // Include native header only once, regardless of how many backends are defined
+ #include "GLFW/glfw3native.h" // Include native header only once, regardless of how many backends are defined
// Required for: glfwGetX11Window() and glfwGetWaylandWindow()
-
- #if defined(_GLFW_X11) // Clean up X11-specific hacks
- #undef Font // Revert hack and allow normal raylib Font usage
+ #if defined(_GLFW_X11) // Clean up X11-specific hacks
+ #undef Font // Revert hack and allow normal raylib Font usage
+ #endif
#endif
#endif
-#endif
#if defined(__APPLE__)
#include // Required for: usleep()
@@ -131,16 +130,17 @@ int InitPlatform(void); // Initialize platform (graphics, inputs and mo
void ClosePlatform(void); // Close platform
// Error callback event
-static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error
+static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error
// Window callbacks events
static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized
-static void WindowPosCallback(GLFWwindow* window, int x, int y); // GLFW3 WindowPos Callback, runs when window is moved
+static void FramebufferSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 FramebufferSize Callback, runs when window is resized
+static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley); // GLFW3 Window Content Scale Callback, runs when window changes scale
+static void WindowPosCallback(GLFWwindow *window, int x, int y); // GLFW3 WindowPos Callback, runs when window is moved
static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored
-static void WindowMaximizeCallback(GLFWwindow* window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized
+static void WindowMaximizeCallback(GLFWwindow *window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized
static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus
static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window
-static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley); // GLFW3 Window Content Scale Callback, runs when window changes scale
// Input callbacks events
static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed
@@ -156,8 +156,6 @@ static void *AllocateWrapper(size_t size, void *user);
static void *ReallocateWrapper(void *block, size_t size, void *user); // GLFW3 GLFWreallocatefun, wrapps around RL_REALLOC macro
static void DeallocateWrapper(void *block, void *user); // GLFW3 GLFWdeallocatefun, wraps around RL_FREE macro
-static void SetDimensionsFromMonitor(GLFWmonitor *monitor); // Set screen dimensions from monitor/display dimensions
-
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
@@ -178,85 +176,92 @@ 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;
- CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
+ CORE.Window.position = (Point){ 0, 0 };
+ CORE.Window.screen = (Size){ CORE.Window.display.width, CORE.Window.display.height };
- glfwSetWindowMonitor(platform.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
- }
- else
- {
- CORE.Window.fullscreen = true;
- CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
+ // Set fullscreen flag to be processed on FramebufferSizeCallback() accordingly
+ FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+ // WARNING: This function launches FramebufferSizeCallback()
glfwSetWindowMonitor(platform.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
}
-
+ else TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor");
}
else
{
- CORE.Window.fullscreen = false;
- CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
+ // Restore previous window position and size
+ CORE.Window.position = CORE.Window.previousPosition;
+ CORE.Window.screen = CORE.Window.previousScreen;
- glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
+ // Set fullscreen flag to be processed on FramebufferSizeCallback() accordingly
+ // and considered by GetWindowScaleDPI()
+ FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
- // we update the window position right away
- CORE.Window.position.x = CORE.Window.previousPosition.x;
- CORE.Window.position.y = CORE.Window.previousPosition.y;
+#if !defined(__APPLE__)
+ // Make sure to restore render size considering HighDPI scaling
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
+ {
+ Vector2 scaleDpi = GetWindowScaleDPI();
+ CORE.Window.screen.width *= scaleDpi.x;
+ CORE.Window.screen.height *= scaleDpi.y;
+ }
+#endif
+
+ glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.position.x, CORE.Window.position.y,
+ CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
}
// Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
// NOTE: V-Sync can be enabled by graphic driver configuration
- if (CORE.Window.flags & FLAG_VSYNC_HINT) glfwSwapInterval(1);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) glfwSwapInterval(1);
}
// Toggle borderless windowed mode
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();
- const int monitor = GetCurrentMonitor();
- int monitorCount;
+ int monitorCount = 0;
GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
+ const int monitor = GetCurrentMonitor();
if ((monitor >= 0) && (monitor < monitorCount))
{
const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
- if (mode)
+ if (mode != NULL)
{
- if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE))
+ if (!FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
// 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
glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE);
- CORE.Window.flags |= FLAG_WINDOW_UNDECORATED;
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED);
// Get monitor position and size
int monitorPosX = 0;
@@ -266,32 +271,42 @@ void ToggleBorderlessWindowed(void)
const int monitorHeight = mode->height;
// Set screen position and size
- glfwSetWindowPos(platform.handle, monitorPosX, monitorPosY);
- glfwSetWindowSize(platform.handle, monitorWidth, monitorHeight);
+ glfwSetWindowMonitor(platform.handle, monitors[monitor], monitorPosX, monitorPosY,
+ monitorWidth, monitorHeight, mode->refreshRate);
// Refocus window
glfwFocusWindow(platform.handle);
- CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE;
+ FLAG_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
}
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);
- CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNDECORATED);
- // 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
- glfwSetWindowSize(platform.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height);
- glfwSetWindowPos(platform.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y);
+ #if !defined(__APPLE__)
+ // Make sure to restore size considering HighDPI scaling
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
+ {
+ Vector2 scaleDpi = GetWindowScaleDPI();
+ CORE.Window.screen.width *= scaleDpi.x;
+ CORE.Window.screen.height *= scaleDpi.y;
+ }
+ #endif
+
+ // 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);
- CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
-
- CORE.Window.position.x = CORE.Window.previousPosition.x;
- CORE.Window.position.y = CORE.Window.previousPosition.y;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
}
}
else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
@@ -305,7 +320,7 @@ void MaximizeWindow(void)
if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
{
glfwMaximizeWindow(platform.handle);
- CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
}
}
@@ -323,8 +338,8 @@ void RestoreWindow(void)
{
// Restores the specified window if it was previously iconified (minimized) or maximized
glfwRestoreWindow(platform.handle);
- CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
- CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
}
}
@@ -337,109 +352,109 @@ void SetWindowState(unsigned int flags)
// NOTE: In most cases the functions already change the flags internally
// State change: FLAG_VSYNC_HINT
- if (((CORE.Window.flags & FLAG_VSYNC_HINT) != (flags & FLAG_VSYNC_HINT)) && ((flags & FLAG_VSYNC_HINT) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT) != FLAG_IS_SET(flags, FLAG_VSYNC_HINT)) && FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
{
glfwSwapInterval(1);
- CORE.Window.flags |= FLAG_VSYNC_HINT;
+ FLAG_SET(CORE.Window.flags, FLAG_VSYNC_HINT);
}
// State change: FLAG_BORDERLESS_WINDOWED_MODE
// NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running
- if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) != (flags & FLAG_BORDERLESS_WINDOWED_MODE)) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE) != FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE)) && FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function
}
// State change: FLAG_FULLSCREEN_MODE
- if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE) && ((flags & FLAG_FULLSCREEN_MODE) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE) != FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE)) && FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
{
ToggleFullscreen(); // NOTE: Window state flag updated inside function
}
// State change: FLAG_WINDOW_RESIZABLE
- if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)) && FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
{
glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_TRUE);
- CORE.Window.flags |= FLAG_WINDOW_RESIZABLE;
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE);
}
// State change: FLAG_WINDOW_UNDECORATED
- if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) != (flags & FLAG_WINDOW_UNDECORATED)) && (flags & FLAG_WINDOW_UNDECORATED))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED) != FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED)) && FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
{
glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE);
- CORE.Window.flags |= FLAG_WINDOW_UNDECORATED;
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED);
}
// State change: FLAG_WINDOW_HIDDEN
- if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) != (flags & FLAG_WINDOW_HIDDEN)) && ((flags & FLAG_WINDOW_HIDDEN) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN) != FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN)) && FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
{
glfwHideWindow(platform.handle);
- CORE.Window.flags |= FLAG_WINDOW_HIDDEN;
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN);
}
// State change: FLAG_WINDOW_MINIMIZED
- if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) != (flags & FLAG_WINDOW_MINIMIZED)) && ((flags & FLAG_WINDOW_MINIMIZED) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED) != FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED)) && FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
{
//GLFW_ICONIFIED
MinimizeWindow(); // NOTE: Window state flag updated inside function
}
// State change: FLAG_WINDOW_MAXIMIZED
- if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED) != FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED)) && FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED))
{
//GLFW_MAXIMIZED
MaximizeWindow(); // NOTE: Window state flag updated inside function
}
// State change: FLAG_WINDOW_UNFOCUSED
- if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) != (flags & FLAG_WINDOW_UNFOCUSED)) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED) != FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED)) && FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
{
glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE);
- CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
}
// State change: FLAG_WINDOW_TOPMOST
- if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) != (flags & FLAG_WINDOW_TOPMOST)) && ((flags & FLAG_WINDOW_TOPMOST) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TOPMOST) != FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST)) && FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
{
glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE);
- CORE.Window.flags |= FLAG_WINDOW_TOPMOST;
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_TOPMOST);
}
// State change: FLAG_WINDOW_ALWAYS_RUN
- if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) != (flags & FLAG_WINDOW_ALWAYS_RUN)) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN) != FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN)) && FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN))
{
- CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN;
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN);
}
// The following states can not be changed after window creation
// State change: FLAG_WINDOW_TRANSPARENT
- if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) != (flags & FLAG_WINDOW_TRANSPARENT)) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TRANSPARENT) != FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT)) && FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
{
TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
}
// State change: FLAG_WINDOW_HIGHDPI
- if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) != (flags & FLAG_WINDOW_HIGHDPI)) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI) != FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI)) && FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
{
TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
}
// State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
- if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) != (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MOUSE_PASSTHROUGH) != FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH)) && FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
{
glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
- CORE.Window.flags |= FLAG_WINDOW_MOUSE_PASSTHROUGH;
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MOUSE_PASSTHROUGH);
}
// State change: FLAG_MSAA_4X_HINT
- if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) != (flags & FLAG_MSAA_4X_HINT)) && ((flags & FLAG_MSAA_4X_HINT) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT) != FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT)) && FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
{
TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization");
}
// State change: FLAG_INTERLACED_HINT
- if (((CORE.Window.flags & FLAG_INTERLACED_HINT) != (flags & FLAG_INTERLACED_HINT)) && ((flags & FLAG_INTERLACED_HINT) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_INTERLACED_HINT) != FLAG_IS_SET(flags, FLAG_INTERLACED_HINT)) && FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
{
TRACELOG(LOG_WARNING, "WINDOW: Interlaced mode can only be configured before window initialization");
}
@@ -452,107 +467,107 @@ void ClearWindowState(unsigned int flags)
// NOTE: In most cases the functions already change the flags internally
// State change: FLAG_VSYNC_HINT
- if (((CORE.Window.flags & FLAG_VSYNC_HINT) > 0) && ((flags & FLAG_VSYNC_HINT) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) && (FLAG_IS_SET(flags, FLAG_VSYNC_HINT)))
{
glfwSwapInterval(0);
- CORE.Window.flags &= ~FLAG_VSYNC_HINT;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_VSYNC_HINT);
}
// State change: FLAG_BORDERLESS_WINDOWED_MODE
// NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running
- if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) && (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE)))
{
- ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function
+ ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function
}
// State change: FLAG_FULLSCREEN_MODE
- if (((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) && ((flags & FLAG_FULLSCREEN_MODE) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) && (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE)))
{
- ToggleFullscreen(); // NOTE: Window state flag updated inside function
+ ToggleFullscreen(); // NOTE: Window state flag updated inside function
}
// State change: FLAG_WINDOW_RESIZABLE
- if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) && (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)))
{
glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_FALSE);
- CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_RESIZABLE);
}
// State change: FLAG_WINDOW_HIDDEN
- if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) && (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN)))
{
glfwShowWindow(platform.handle);
- CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_HIDDEN);
}
// State change: FLAG_WINDOW_MINIMIZED
- if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((flags & FLAG_WINDOW_MINIMIZED) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) && (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED)))
{
RestoreWindow(); // NOTE: Window state flag updated inside function
}
// State change: FLAG_WINDOW_MAXIMIZED
- if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) && (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED)))
{
RestoreWindow(); // NOTE: Window state flag updated inside function
}
// State change: FLAG_WINDOW_UNDECORATED
- if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED)) && (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED)))
{
glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE);
- CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNDECORATED);
}
// State change: FLAG_WINDOW_UNFOCUSED
- if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) && (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED)))
{
glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE);
- CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
}
// State change: FLAG_WINDOW_TOPMOST
- if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TOPMOST)) && (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST)))
{
glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE);
- CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_TOPMOST);
}
// State change: FLAG_WINDOW_ALWAYS_RUN
- if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) > 0) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN)) && (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN)))
{
- CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN);
}
// The following states can not be changed after window creation
// State change: FLAG_WINDOW_TRANSPARENT
- if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TRANSPARENT)) && (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT)))
{
TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
}
// State change: FLAG_WINDOW_HIGHDPI
- if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) && (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI)))
{
TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
}
// State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
- if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MOUSE_PASSTHROUGH)) && (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH)))
{
glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE);
- CORE.Window.flags &= ~FLAG_WINDOW_MOUSE_PASSTHROUGH;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MOUSE_PASSTHROUGH);
}
// State change: FLAG_MSAA_4X_HINT
- if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) > 0) && ((flags & FLAG_MSAA_4X_HINT) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) && (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT)))
{
TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization");
}
// State change: FLAG_INTERLACED_HINT
- if (((CORE.Window.flags & FLAG_INTERLACED_HINT) > 0) && ((flags & FLAG_INTERLACED_HINT) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_INTERLACED_HINT)) && (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT)))
{
TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
}
@@ -645,7 +660,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]));
@@ -881,7 +896,8 @@ Vector2 GetMonitorPosition(int monitor)
if ((monitor >= 0) && (monitor < monitorCount))
{
- int x, y;
+ int x = 0;
+ int y = 0;
glfwGetMonitorPos(monitors[monitor], &x, &y);
return (Vector2){ (float)x, (float)y };
@@ -1002,8 +1018,9 @@ Vector2 GetWindowPosition(void)
// Get window scale DPI factor for current monitor
Vector2 GetWindowScaleDPI(void)
{
- Vector2 scale = { 0 };
- glfwGetWindowContentScale(platform.handle, &scale.x, &scale.y);
+ Vector2 scale = { 1.0f, 1.0f };
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI) && !FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
+ glfwGetWindowContentScale(platform.handle, &scale.x, &scale.y);
return scale;
}
@@ -1070,6 +1087,7 @@ void EnableCursor(void)
if (glfwRawMouseMotionSupported()) glfwSetInputMode(platform.handle, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE);
+ CORE.Input.Mouse.cursorHidden = false;
CORE.Input.Mouse.cursorLocked = false;
}
@@ -1083,6 +1101,7 @@ void DisableCursor(void)
if (glfwRawMouseMotionSupported()) glfwSetInputMode(platform.handle, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
+ CORE.Input.Mouse.cursorHidden = true;
CORE.Input.Mouse.cursorLocked = true;
}
@@ -1107,7 +1126,7 @@ double GetTime(void)
// 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
-// Ref: https://github.com/raysan5/raylib/issues/686
+// REF: https://github.com/raysan5/raylib/issues/686
void OpenURL(const char *url)
{
// Security check to (partially) avoid malicious code
@@ -1218,9 +1237,9 @@ void PollInputEvents(void)
// Map touch position to mouse position for convenience
// WARNING: If the target desktop device supports touch screen, this behaviour should be reviewed!
- // TODO: GLFW does not support multi-touch input just yet
- // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch
- // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages
+ // TODO: GLFW does not support multi-touch input yet
+ // REF: https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch
+ // REF: https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages
CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
// Check if gamepads are ready
@@ -1242,8 +1261,14 @@ void PollInputEvents(void)
// Get current gamepad state
// NOTE: There is no callback available, so we get it manually
GLFWgamepadstate state = { 0 };
- glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller
-
+ int 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;
for (int k = 0; (buttons != NULL) && (k < MAX_GAMEPAD_BUTTONS); k++)
@@ -1313,7 +1338,8 @@ void PollInputEvents(void)
CORE.Window.resizedLastFrame = false;
- if ((CORE.Window.eventWaiting) || (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)))
+ 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)
CORE.Time.previous = GetTime();
@@ -1332,7 +1358,7 @@ 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
-// https://www.glfw.org/docs/latest/intro_guide.html#init_allocator
+// REF: https://www.glfw.org/docs/latest/intro_guide.html#init_allocator
static void *AllocateWrapper(size_t size, void *user)
{
(void)user;
@@ -1391,64 +1417,58 @@ int InitPlatform(void)
unsigned int requestedWindowFlags = CORE.Window.flags;
// Check window creation flags
- if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true;
-
- if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window
+ 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
- if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED)) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window
- if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable
// Disable FLAG_WINDOW_MINIMIZED, not supported on initialization
- if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
// Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization
- if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
- if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
- if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TOPMOST)) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
// NOTE: Some GLFW flags are not supported on HTML5
- if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); // Transparent framebuffer
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TRANSPARENT)) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); // Transparent framebuffer
else glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE); // Opaque framebuffer
- // HACK: Most of this was written before GLFW_SCALE_FRAMEBUFFER existed and
- // was enabled by default. Disabling it gets back the old behavior. A
- // complete fix will require removing a lot of CORE.Window.render
- // manipulation code
- // NOTE: This currently doesn't work on macOS(see #5185), so we skip it there
- // when FLAG_WINDOW_HIGHDPI is *unset*
-#if !defined(__APPLE__)
- glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE);
-#endif
-
- if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
{
- // since we skipped it before, now make sure to set this on macOS
#if defined(__APPLE__)
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 pixels always map 1:1 such as Windows and X11
+ // 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); // Scale content area based on the monitor content scale where window is placed on
+ glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
#if defined(__APPLE__)
glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE);
#endif
}
- else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
+ else
+ {
+ glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
+#if defined(__APPLE__)
+ glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE);
+#endif
+ }
// Mouse passthrough
- if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MOUSE_PASSTHROUGH)) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
else glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE);
- if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
{
// NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs
TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
@@ -1509,69 +1529,40 @@ int InitPlatform(void)
// REF: https://github.com/raysan5/raylib/issues/1554
glfwSetJoystickCallback(NULL);
- GLFWmonitor *monitor = NULL;
- if (CORE.Window.fullscreen)
- {
- // According to glfwCreateWindow(), if the user does not have a choice, fullscreen applications
- // should default to the primary monitor
+ if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0)) FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
- monitor = glfwGetPrimaryMonitor();
+ // 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
+ GLFWmonitor *monitor = glfwGetPrimaryMonitor();
if (!monitor)
{
- TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor");
- return -1;
+ TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor");
+ return -1;
}
- SetDimensionsFromMonitor(monitor);
+ // Set dimensions from monitor
+ const GLFWvidmode *mode = glfwGetVideoMode(monitor);
- // 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))
+ // Default display resolution to that of the current mode
+ CORE.Window.display.width = mode->width;
+ CORE.Window.display.height = mode->height;
+
+ // 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;
- }
- 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;
+ // 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;
}
- 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);
+ // Set screen width/height to the display width/height
+ if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width;
+ if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height;
platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", monitor, NULL);
if (!platform.handle)
@@ -1580,20 +1571,14 @@ int InitPlatform(void)
TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
return -1;
}
-
- // NOTE: Full-screen change, not working properly...
- //glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
}
else
{
- // 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();
@@ -1602,7 +1587,7 @@ int InitPlatform(void)
}
// After the window was created, determine the monitor that the window manager assigned
- // Derive display sizes, and, if possible, window size in case it was zero at beginning
+ // Derive display sizes and, if possible, window size in case it was zero at beginning
int monitorCount = 0;
int monitorIndex = GetCurrentMonitor();
@@ -1610,10 +1595,18 @@ int InitPlatform(void)
if (monitorIndex < monitorCount)
{
- monitor = monitors[monitorIndex];
- SetDimensionsFromMonitor(monitor);
+ GLFWmonitor *monitor = monitors[monitorIndex];
+ const GLFWvidmode *mode = glfwGetVideoMode(monitor);
- if (requestWindowedFullscreen) glfwSetWindowSize(platform.handle, CORE.Window.screen.width, CORE.Window.screen.height);
+ // Default display resolution to that of the current mode
+ 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;
+
+ glfwSetWindowSize(platform.handle, CORE.Window.screen.width, CORE.Window.screen.height);
}
else
{
@@ -1623,6 +1616,7 @@ int InitPlatform(void)
return -1;
}
+ // NOTE: Not considering scale factor now, considered below
CORE.Window.render.width = CORE.Window.screen.width;
CORE.Window.render.height = CORE.Window.screen.height;
}
@@ -1640,7 +1634,7 @@ int InitPlatform(void)
// 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
// to be activated on web platforms since VSync is enforced there
- if (CORE.Window.flags & FLAG_VSYNC_HINT)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT))
{
// WARNING: It seems to hit a critical render path in Intel HD Graphics
glfwSwapInterval(1);
@@ -1650,19 +1644,20 @@ int InitPlatform(void)
int fbWidth = CORE.Window.screen.width;
int fbHeight = CORE.Window.screen.height;
- if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
{
// NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling
- // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE);
- #if !defined(__APPLE__)
+ // 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
CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f);
-
+#if !defined(__APPLE__)
// Mouse input scaling for the new screen size
SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight);
- #endif
+#endif
}
CORE.Window.render.width = fbWidth;
@@ -1682,12 +1677,17 @@ int InitPlatform(void)
return -1;
}
- if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
+ 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
{
+ int monitorCount = 0;
+ int monitorIndex = GetCurrentMonitor();
+ GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
+ GLFWmonitor *monitor = monitors[monitorIndex];
+
// Try to center window on screen but avoiding window-bar outside of screen
int monitorX = 0;
int monitorY = 0;
@@ -1695,7 +1695,7 @@ int InitPlatform(void)
int monitorHeight = 0;
glfwGetMonitorWorkarea(monitor, &monitorX, &monitorY, &monitorWidth, &monitorHeight);
- // Here CORE.Window.render.width/height should be used instead of
+ // TODO: Here CORE.Window.render.width/height should be used instead of
// CORE.Window.screen.width/height to center the window correctly when the high dpi flag is enabled
int posX = monitorX + (monitorWidth - (int)CORE.Window.render.width)/2;
int posY = monitorY + (monitorHeight - (int)CORE.Window.render.height)/2;
@@ -1719,35 +1719,36 @@ int InitPlatform(void)
// Initialize input events callbacks
//----------------------------------------------------------------------------
// Set window callback events
- glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default!
+ glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing is not enabled by default
+ glfwSetFramebufferSizeCallback(platform.handle, FramebufferSizeCallback);
glfwSetWindowPosCallback(platform.handle, WindowPosCallback);
glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback);
glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback);
glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback);
glfwSetDropCallback(platform.handle, WindowDropCallback);
-
- if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
- {
- glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback);
- }
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback);
// Set input callback events
glfwSetKeyCallback(platform.handle, KeyCallback);
glfwSetCharCallback(platform.handle, CharCallback);
glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback);
- glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes
+ glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes
glfwSetScrollCallback(platform.handle, MouseScrollCallback);
glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback);
glfwSetJoystickCallback(JoystickCallback);
-
- glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM)
+ glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM)
// Retrieve gamepad names
for (int i = 0; i < MAX_GAMEPADS; i++)
{
// WARNING: If glfwGetJoystickName() is longer than MAX_GAMEPAD_NAME_LENGTH,
// we can get a not-NULL terminated string, so, we only copy up to (MAX_GAMEPAD_NAME_LENGTH - 1)
- if (glfwJoystickPresent(i)) strncpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i), MAX_GAMEPAD_NAME_LENGTH - 1);
+ if (glfwJoystickPresent(i))
+ {
+ CORE.Input.Gamepad.ready[i] = true;
+ CORE.Input.Gamepad.axisCount[i] = GLFW_GAMEPAD_AXIS_LAST + 1;
+ strncpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i), MAX_GAMEPAD_NAME_LENGTH - 1);
+ }
}
//----------------------------------------------------------------------------
@@ -1793,79 +1794,134 @@ void ClosePlatform(void)
#endif
}
-// GLFW3 Error Callback, runs on GLFW3 error
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+// NOTE: Those functions are only required for current platform
+//----------------------------------------------------------------------------------
+
+// GLFW3: Error callback, runs on GLFW3 error
static void ErrorCallback(int error, const char *description)
{
TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description);
}
-// GLFW3 WindowSize Callback, runs when window is resizedLastFrame
+// GLFW3: Window size change callback, runs when window is resized
// NOTE: Window resizing not enabled by default, use SetConfigFlags()
static void WindowSizeCallback(GLFWwindow *window, int width, int height)
{
+ // Nothing to do for now on window resize...
+ //TRACELOG(LOG_INFO, "GLFW3: Window size callback called [%i,%i]", width, height);
+}
+
+// GLFW3: Framebuffer size change callback, runs when framebuffer is resized
+// WARNING: If FLAG_WINDOW_HIGHDPI is set, WindowContentScaleCallback() is called before this function
+static void FramebufferSizeCallback(GLFWwindow *window, int width, int height)
+{
+ TRACELOG(LOG_INFO, "GLFW3: Window framebuffer size callback called [%i,%i]", width, height);
+
// WARNING: On window minimization, callback is called,
// but we don't want to change internal screen values, it breaks things
if ((width == 0) || (height == 0)) return;
// Reset viewport and projection matrix for new size
+ // NOTE: Stores current render size: CORE.Window.render
SetupViewport(width, height);
+ // Set render size
CORE.Window.currentFbo.width = width;
CORE.Window.currentFbo.height = height;
CORE.Window.resizedLastFrame = true;
- if (IsWindowFullscreen()) return;
-
- // if we are doing automatic DPI scaling, then the "screen" size is divided by the window scale
- if (IsWindowState(FLAG_WINDOW_HIGHDPI))
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
{
- width = (int)(width/GetWindowScaleDPI().x);
- height = (int)(height/GetWindowScaleDPI().y);
+ // 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;
+ }
}
-
- // Set render size
- CORE.Window.render.width = width;
- CORE.Window.render.height = height;
-
- // Set current screen size
- CORE.Window.screen.width = width;
- CORE.Window.screen.height = height;
// WARNING: If using a render texture, it is not scaled to new size
}
-static void WindowPosCallback(GLFWwindow* window, int x, int y)
+
+// GLFW3: Window content scale callback, runs on monitor content scale change detected
+// 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);
+
+ float fbWidth = (float)CORE.Window.screen.width*scalex;
+ float fbHeight = (float)CORE.Window.screen.height*scaley;
+
+ // 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);
+ CORE.Window.screenScale = MatrixScale(scalex, scaley, 1.0f);
+
+#if !defined(__APPLE__)
+ // Mouse input scaling for the new screen size
+ SetMouseScale(1.0f/scalex, 1.0f/scaley);
+#endif
+
+ CORE.Window.render.width = (int)fbWidth;
+ CORE.Window.render.height = (int)fbHeight;
+ CORE.Window.currentFbo = CORE.Window.render;
+}
+
+// GLFW3: Window position callback, runs when window position changes
+static void WindowPosCallback(GLFWwindow *window, int x, int y)
{
// Set current window position
CORE.Window.position.x = x;
CORE.Window.position.y = y;
}
-static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley)
-{
- CORE.Window.screenScale = MatrixScale(scalex, scaley, 1.0f);
-}
-// GLFW3 WindowIconify Callback, runs when window is minimized/restored
+// GLFW3: Window iconify callback, runs when window is minimized/restored
static void WindowIconifyCallback(GLFWwindow *window, int iconified)
{
- if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified
- else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored
+ if (iconified) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was iconified
+ else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was restored
}
-// GLFW3 WindowMaximize Callback, runs when window is maximized/restored
+// GLFW3: Window maximize callback, runs when window is maximized/restored
static void WindowMaximizeCallback(GLFWwindow *window, int maximized)
{
- if (maximized) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // The window was maximized
- else CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; // The window was restored
+ if (maximized) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); // The window was maximized
+ else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); // The window was restored
}
-// GLFW3 WindowFocus Callback, runs when window get/lose focus
+// GLFW3: Window focus callback, runs when window get/lose focus
static void WindowFocusCallback(GLFWwindow *window, int focused)
{
- if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // The window was focused
- else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; // The window lost focus
+ if (focused) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window was focused
+ else FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window lost focus
}
-// GLFW3 Window Drop Callback, runs when drop files into window
+// GLFW3: Window drop callback, runs when files are dropped into window
static void WindowDropCallback(GLFWwindow *window, int count, const char **paths)
{
if (count > 0)
@@ -1888,12 +1944,12 @@ 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);
}
}
}
-// GLFW3 Keyboard Callback, runs on key pressed
+// GLFW3: Keyboard callback, runs on key pressed
static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
{
if (key < 0) return; // Security check, macOS fn key generates -1
@@ -1905,8 +1961,8 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i
else if (action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
// WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys
- if (((key == KEY_CAPS_LOCK) && ((mods & GLFW_MOD_CAPS_LOCK) > 0)) ||
- ((key == KEY_NUM_LOCK) && ((mods & GLFW_MOD_NUM_LOCK) > 0))) CORE.Input.Keyboard.currentKeyState[key] = 1;
+ if (((key == KEY_CAPS_LOCK) && (FLAG_IS_SET(mods, GLFW_MOD_CAPS_LOCK))) ||
+ ((key == KEY_NUM_LOCK) && (FLAG_IS_SET(mods, GLFW_MOD_NUM_LOCK)))) CORE.Input.Keyboard.currentKeyState[key] = 1;
// Check if there is space available in the key queue
if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS))
@@ -1920,13 +1976,13 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i
if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE);
}
-// GLFW3 Char Callback, get unicode codepoint value
+// GLFW3: Char callback, runs on key pressed to get unicode codepoint value
static void CharCallback(GLFWwindow *window, unsigned int codepoint)
{
// NOTE: Registers any key down considering OS keyboard layout but
// does not detect action events, those should be managed by user...
- // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
- // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char
+ // REF: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
+ // REF: https://www.glfw.org/docs/latest/input_guide.html#input_char
// Check if there is space available in the queue
if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
@@ -1937,7 +1993,7 @@ static void CharCallback(GLFWwindow *window, unsigned int codepoint)
}
}
-// GLFW3 Mouse Button Callback, runs on mouse button pressed
+// GLFW3: Mouse button callback, runs on mouse button pressed
static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
{
// WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now,
@@ -1973,7 +2029,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int
#endif
}
-// GLFW3 Cursor Position Callback, runs on mouse move
+// GLFW3: Cursor position callback, runs on mouse movement
static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
{
CORE.Input.Mouse.currentPosition.x = (float)x;
@@ -2004,20 +2060,20 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
#endif
}
-// GLFW3 Scrolling Callback, runs on mouse wheel
+// GLFW3: Mouse wheel scroll callback, runs on mouse wheel changes
static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset)
{
CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset };
}
-// GLFW3 CursorEnter Callback, when cursor enters the window
+// GLFW3: Cursor ennter callback, when cursor enters the window
static void CursorEnterCallback(GLFWwindow *window, int enter)
{
if (enter) CORE.Input.Mouse.cursorOnScreen = true;
else CORE.Input.Mouse.cursorOnScreen = false;
}
-// GLFW3 Joystick Connected/Disconnected Callback
+// GLFW3: Joystick connected/disconnected callback
static void JoystickCallback(int jid, int event)
{
if (event == GLFW_CONNECTED)
@@ -2033,20 +2089,6 @@ static void JoystickCallback(int jid, int event)
}
}
-// Set screen dimensions from monitor/display dimensions
-static void SetDimensionsFromMonitor(GLFWmonitor *monitor)
-{
- const GLFWvidmode *mode = glfwGetVideoMode(monitor);
-
- // Default display resolution to that of the current mode
- 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;
-}
-
#ifdef _WIN32
# define WIN32_CLIPBOARD_IMPLEMENTATION
# include "../external/win32_clipboard.h"
diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c
index 86842140d..558b6de55 100644
--- a/src/platforms/rcore_desktop_rgfw.c
+++ b/src/platforms/rcore_desktop_rgfw.c
@@ -48,11 +48,6 @@
*
**********************************************************************************************/
-#ifndef RAYLIB_H /* this should never actually happen, it's only here for IDEs */
-#include "raylib.h"
-#include "../rcore.c"
-#endif
-
#if defined(PLATFORM_WEB_RGFW)
#define RGFW_NO_GL_HEADER
#endif
@@ -295,23 +290,21 @@ 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;
- CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
+ FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
RGFW_monitor_scaleToWindow(platform.mon, platform.window);
RGFW_window_setFullscreen(platform.window, 1);
}
else
{
- CORE.Window.fullscreen = false;
- CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
if (platform.mon.mode.area.w)
{
@@ -330,13 +323,15 @@ void ToggleFullscreen(void)
// 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
- if (CORE.Window.flags & FLAG_VSYNC_HINT) RGFW_window_swapInterval(platform.window, 1);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) RGFW_window_swapInterval(platform.window, 1);
}
// 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;
@@ -353,8 +348,6 @@ void ToggleBorderlessWindowed(void)
CORE.Window.position = CORE.Window.previousPosition;
RGFW_window_resize(platform.window, RGFW_AREA(CORE.Window.previousScreen.width, CORE.Window.previousScreen.height));
}
-
- CORE.Window.fullscreen = !CORE.Window.fullscreen;
}
// Set window state: maximized, if resizable
@@ -372,7 +365,7 @@ void MinimizeWindow(void)
// Restore window from being minimized/maximized
void RestoreWindow(void)
{
- if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
+ if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
RGFW_window_restore(platform.window);
}
@@ -382,72 +375,72 @@ void SetWindowState(unsigned int flags)
{
if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead");
- CORE.Window.flags |= flags;
+ FLAG_SET(CORE.Window.flags, flags);
- if (flags & FLAG_VSYNC_HINT)
+ if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
{
RGFW_window_swapInterval(platform.window, 1);
}
- if (flags & FLAG_FULLSCREEN_MODE)
+ if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
{
- if (!CORE.Window.fullscreen) ToggleFullscreen();
+ ToggleFullscreen();
}
- if (flags & FLAG_WINDOW_RESIZABLE)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
{
RGFW_window_setMaxSize(platform.window, RGFW_AREA(0, 0));
RGFW_window_setMinSize(platform.window, RGFW_AREA(0, 0));
}
- if (flags & FLAG_WINDOW_UNDECORATED)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
{
RGFW_window_setBorder(platform.window, 0);
}
- if (flags & FLAG_WINDOW_HIDDEN)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
{
RGFW_window_hide(platform.window);
}
- if (flags & FLAG_WINDOW_MINIMIZED)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
{
RGFW_window_minimize(platform.window);
}
- if (flags & FLAG_WINDOW_MAXIMIZED)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED))
{
RGFW_window_maximize(platform.window);
}
- if (flags & FLAG_WINDOW_UNFOCUSED)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
{
- CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
- platform.window->_flags &= ~RGFW_windowFocusOnShow;
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
+ FLAG_CLEAR(platform.window->_flags, RGFW_windowFocusOnShow);
RGFW_window_setFlags(platform.window, platform.window->_flags);
}
- if (flags & FLAG_WINDOW_TOPMOST)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
{
RGFW_window_setFloating(platform.window, RGFW_TRUE);
}
- if (flags & FLAG_WINDOW_ALWAYS_RUN)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN))
{
- CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN;
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN);
}
- if (flags & FLAG_WINDOW_TRANSPARENT)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
{
TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
}
- if (flags & FLAG_WINDOW_HIGHDPI)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
{
TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
}
- if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
{
RGFW_window_setMousePassthrough(platform.window, 1);
}
- if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
+ if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
ToggleBorderlessWindowed();
}
- if (flags & FLAG_MSAA_4X_HINT)
+ if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
{
RGFW_setGLHint(RGFW_glSamples, 4);
}
- if (flags & FLAG_INTERLACED_HINT)
+ if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
{
TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
}
@@ -456,122 +449,86 @@ void SetWindowState(unsigned int flags)
// Clear window configuration state flags
void ClearWindowState(unsigned int flags)
{
- CORE.Window.flags &= ~flags;
+ FLAG_CLEAR(CORE.Window.flags, flags);
- if (flags & FLAG_VSYNC_HINT)
+ if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
{
RGFW_window_swapInterval(platform.window, 0);
}
- if (flags & FLAG_FULLSCREEN_MODE)
+ if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
{
- if (CORE.Window.fullscreen) ToggleFullscreen();
+ ToggleFullscreen();
}
- if (flags & FLAG_WINDOW_RESIZABLE)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
{
RGFW_window_setMaxSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h));
RGFW_window_setMinSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h));
}
- if (flags & FLAG_WINDOW_UNDECORATED)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
{
RGFW_window_setBorder(platform.window, 1);
}
- if (flags & FLAG_WINDOW_HIDDEN)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
{
- if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
+ if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
RGFW_window_show(platform.window);
}
- if (flags & FLAG_WINDOW_MINIMIZED)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
{
- if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
+ if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
RGFW_window_restore(platform.window);
}
- if (flags & FLAG_WINDOW_MAXIMIZED)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED))
{
- if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
+ if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
RGFW_window_restore(platform.window);
}
- if (flags & FLAG_WINDOW_UNFOCUSED)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
{
RGFW_window_setFlags(platform.window, platform.window->_flags | RGFW_windowFocusOnShow);
- CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
}
- if (flags & FLAG_WINDOW_TOPMOST)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
{
RGFW_window_setFloating(platform.window, RGFW_FALSE);
}
- if (flags & FLAG_WINDOW_ALWAYS_RUN)
- {
- CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN;
- }
- if (flags & FLAG_WINDOW_TRANSPARENT)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
{
TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
}
- if (flags & FLAG_WINDOW_HIGHDPI)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
{
TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
}
- if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
{
RGFW_window_setMousePassthrough(platform.window, 0);
}
- if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
+ if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
- if (CORE.Window.fullscreen) ToggleBorderlessWindowed();
+ ToggleBorderlessWindowed();
}
- if (flags & FLAG_MSAA_4X_HINT)
+ if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
{
RGFW_setGLHint(RGFW_glSamples, 0);
}
- if (flags & FLAG_INTERLACED_HINT)
+ if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
{
TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
}
}
-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
@@ -588,12 +545,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);
}
}
@@ -843,8 +805,9 @@ void EnableCursor(void)
// Set cursor position in the middle
SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
- RGFW_window_showMouse(platform.window, true);
- CORE.Input.Mouse.cursorHidden = false;
+ ShowCursor();
+
+ CORE.Input.Mouse.cursorLocked = true;
}
// Disables cursor (lock cursor)
@@ -853,6 +816,8 @@ void DisableCursor(void)
RGFW_disableCursor = true;
RGFW_window_mouseHold(platform.window, RGFW_AREA(0, 0));
HideCursor();
+
+ CORE.Input.Mouse.cursorLocked = true;
}
// Swap back buffer with front buffer (screen drawing)
@@ -872,17 +837,27 @@ double GetTime(void)
}
// 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.
-// Ref: https://github.com/raysan5/raylib/issues/686
+// NOTE: This function is only safe to use if you control the URL given
+// A user could craft a malicious string performing another action
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
{
- // TODO: Open URL implementation
+ char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char));
+#if defined(_WIN32)
+ sprintf(cmd, "explorer \"%s\"", url);
+#endif
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser
+#endif
+#if defined(__APPLE__)
+ sprintf(cmd, "open '%s'", url);
+#endif
+ int result = system(cmd);
+ if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
+ RL_FREE(cmd);
}
}
@@ -917,7 +892,7 @@ void SetMouseCursor(int cursor)
RGFW_window_setMouseStandard(platform.window, cursor);
}
-// Get physical key name.
+// Get physical key name
const char *GetKeyName(int key)
{
TRACELOG(LOG_WARNING, "GetKeyName() unsupported on target platform");
@@ -983,7 +958,7 @@ void PollInputEvents(void)
CORE.Window.resizedLastFrame = false;
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
- if (platform.window->_flags & RGFW_HOLD_MOUSE)
+ if (FLAG_IS_SET(platform.window->_flags, RGFW_HOLD_MOUSE))
{
CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f };
CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f };
@@ -1062,18 +1037,18 @@ void PollInputEvents(void)
} break;
case RGFW_windowMaximized:
{
- CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // The window was maximized
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); // The window was maximized
} break;
case RGFW_windowMinimized:
{
- CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was iconified
} break;
case RGFW_windowRestored:
{
if (RGFW_window_isMaximized(platform.window))
- CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; // The window was restored
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); // The window was restored
if (RGFW_window_isMinimized(platform.window))
- CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was restored
} break;
case RGFW_windowMoved:
{
@@ -1097,11 +1072,7 @@ void PollInputEvents(void)
CORE.Input.Keyboard.currentKeyState[key] = 1;
}
- // TODO: Put exitKey verification outside the switch?
- if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey])
- {
- CORE.Window.shouldClose = true;
- }
+ if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey]) CORE.Window.shouldClose = true;
// NOTE: event.text.text data comes an UTF-8 text sequence but we register codepoints (int)
// Check if there is space available in the queue
@@ -1159,7 +1130,7 @@ void PollInputEvents(void)
} break;
case RGFW_mousePosChanged:
{
- if (platform.window->_flags & RGFW_HOLD_MOUSE)
+ if (FLAG_IS_SET(platform.window->_flags, RGFW_HOLD_MOUSE))
{
CORE.Input.Mouse.currentPosition.x += (float)event->vector.x;
CORE.Input.Mouse.currentPosition.y += (float)event->vector.y;
@@ -1283,24 +1254,22 @@ int InitPlatform(void)
unsigned int flags = RGFW_windowCenter | RGFW_windowAllowDND;
// Check window creation flags
- if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
{
- CORE.Window.fullscreen = true;
- flags |= RGFW_windowFullscreen;
+ FLAG_SET(flags, RGFW_windowFullscreen);
}
- if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
- CORE.Window.fullscreen = true;
- flags |= RGFW_windowedFullscreen;
+ FLAG_SET(flags, RGFW_windowedFullscreen);
}
- if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) flags |= RGFW_windowNoBorder;
- if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) flags |= RGFW_windowNoResize;
- if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) flags |= RGFW_windowTransparent;
- if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) flags |= RGFW_windowFullscreen;
- if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) flags |= RGFW_windowHide;
- if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) flags |= RGFW_windowMaximize;
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED)) FLAG_SET(flags, RGFW_windowNoBorder);
+ if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) FLAG_SET(flags, RGFW_windowNoResize);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TRANSPARENT)) FLAG_SET(flags, RGFW_windowTransparent);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) FLAG_SET(flags, RGFW_windowFullscreen);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) FLAG_SET(flags, RGFW_windowHide);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_SET(flags, RGFW_windowMaximize);
// NOTE: Some OpenGL context attributes must be set before window creation
// Check selection OpenGL version
@@ -1320,9 +1289,9 @@ int InitPlatform(void)
RGFW_setGLHint(RGFW_glMinor, 3);
}
- if (CORE.Window.flags & FLAG_MSAA_4X_HINT) RGFW_setGLHint(RGFW_glSamples, 4);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) RGFW_setGLHint(RGFW_glSamples, 4);
- if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) flags |= RGFW_windowFocusOnShow | RGFW_windowFocus;
+ if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) FLAG_SET(flags, RGFW_windowFocusOnShow | RGFW_windowFocus);
platform.window = RGFW_createWindow(CORE.Window.title, RGFW_RECT(0, 0, CORE.Window.screen.width, CORE.Window.screen.height), flags);
platform.mon.mode.area.w = 0;
@@ -1342,11 +1311,7 @@ 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 (CORE.Window.flags & FLAG_VSYNC_HINT) RGFW_window_swapInterval(platform.window, 1);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) RGFW_window_swapInterval(platform.window, 1);
RGFW_window_makeCurrent(platform.window);
// Check surface and context activation
diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c
index 03bad80f9..952268ca6 100644
--- a/src/platforms/rcore_desktop_sdl.c
+++ b/src/platforms/rcore_desktop_sdl.c
@@ -57,7 +57,7 @@
// SDL base library (window/rendered, input, timing... functionality)
#ifdef USING_SDL3_PROJECT
#include "SDL3/SDL.h"
-#elif USING_SDL2_PROJECT
+#elif defined(USING_SDL2_PROJECT)
#include "SDL2/SDL.h"
#else
#include "SDL.h"
@@ -71,7 +71,7 @@
// SDL OpenGL functionality (if required, instead of internal renderer)
#ifdef USING_SDL3_PROJECT
#include "SDL3/SDL_opengl.h"
- #elif USING_SDL2_PROJECT
+ #elif defined(USING_SDL2_PROJECT)
#include "SDL2/SDL_opengl.h"
#else
#include "SDL_opengl.h"
@@ -463,22 +463,20 @@ void ToggleFullscreen(void)
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
- if ((monitor > 0) && (monitor <= monitorCount))
+ if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
{
- if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
{
SDL_SetWindowFullscreen(platform.window, 0);
- CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
- CORE.Window.fullscreen = false;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
}
else
{
SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN);
- CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
- CORE.Window.fullscreen = true;
+ FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
}
}
else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
@@ -491,20 +489,20 @@ void ToggleBorderlessWindowed(void)
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
- if ((monitor > 0) && (monitor <= monitorCount))
+ if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
{
- if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
SDL_SetWindowFullscreen(platform.window, 0);
- CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
}
else
{
SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
- CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE;
+ FLAG_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
}
}
else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
@@ -514,14 +512,14 @@ void ToggleBorderlessWindowed(void)
void MaximizeWindow(void)
{
SDL_MaximizeWindow(platform.window);
- if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) == 0) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
+ if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
}
// Set window state: minimized
void MinimizeWindow(void)
{
SDL_MinimizeWindow(platform.window);
- if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) == 0) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED;
+ if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
}
// Restore window from being minimized/maximized
@@ -536,83 +534,83 @@ void SetWindowState(unsigned int flags)
{
if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead");
- CORE.Window.flags |= flags;
+ FLAG_SET(CORE.Window.flags, flags);
- if (flags & FLAG_VSYNC_HINT)
+ if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
{
SDL_GL_SetSwapInterval(1);
}
- if (flags & FLAG_FULLSCREEN_MODE)
+ if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
{
const int monitor = SDL_GetWindowDisplayIndex(platform.window);
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
- if ((monitor > 0) && (monitor <= monitorCount))
+ if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#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");
}
- if (flags & FLAG_WINDOW_RESIZABLE)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
{
SDL_SetWindowResizable(platform.window, SDL_TRUE);
}
- if (flags & FLAG_WINDOW_UNDECORATED)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
{
SDL_SetWindowBordered(platform.window, SDL_FALSE);
}
- if (flags & FLAG_WINDOW_HIDDEN)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
{
SDL_HideWindow(platform.window);
}
- if (flags & FLAG_WINDOW_MINIMIZED)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
{
SDL_MinimizeWindow(platform.window);
}
- if (flags & FLAG_WINDOW_MAXIMIZED)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED))
{
SDL_MaximizeWindow(platform.window);
}
- if (flags & FLAG_WINDOW_UNFOCUSED)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
{
// NOTE: To be able to implement this part it seems that we should
// do it ourselves, via 'windows.h', 'X11/Xlib.h' or even 'Cocoa.h'
TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL");
}
- if (flags & FLAG_WINDOW_TOPMOST)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
{
SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE);
}
- if (flags & FLAG_WINDOW_ALWAYS_RUN)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN))
{
- CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN;
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN);
}
- if (flags & FLAG_WINDOW_TRANSPARENT)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
{
TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_SDL");
}
- if (flags & FLAG_WINDOW_HIGHDPI)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
{
// NOTE: Such a function does not seem to exist
TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_SDL");
}
- if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
{
//SDL_SetWindowGrab(platform.window, SDL_FALSE);
TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_SDL");
}
- if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
+ if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
const int monitor = SDL_GetWindowDisplayIndex(platform.window);
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
- if ((monitor > 0) && (monitor <= monitorCount))
+ if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@@ -621,12 +619,12 @@ void SetWindowState(unsigned int flags)
}
else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
}
- if (flags & FLAG_MSAA_4X_HINT)
+ if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); // Enable multisampling buffers
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); // Enable multisampling
}
- if (flags & FLAG_INTERLACED_HINT)
+ if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
{
TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_SDL");
}
@@ -635,74 +633,69 @@ void SetWindowState(unsigned int flags)
// Clear window configuration state flags
void ClearWindowState(unsigned int flags)
{
- CORE.Window.flags &= ~flags;
+ FLAG_CLEAR(CORE.Window.flags, flags);
- if (flags & FLAG_VSYNC_HINT)
+ if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
{
SDL_GL_SetSwapInterval(0);
}
- if (flags & FLAG_FULLSCREEN_MODE)
+ if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
{
SDL_SetWindowFullscreen(platform.window, 0);
- CORE.Window.fullscreen = false;
}
- if (flags & FLAG_WINDOW_RESIZABLE)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
{
SDL_SetWindowResizable(platform.window, SDL_FALSE);
}
- if (flags & FLAG_WINDOW_UNDECORATED)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
{
SDL_SetWindowBordered(platform.window, SDL_TRUE);
}
- if (flags & FLAG_WINDOW_HIDDEN)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
{
SDL_ShowWindow(platform.window);
}
- if (flags & FLAG_WINDOW_MINIMIZED)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
{
SDL_RestoreWindow(platform.window);
}
- if (flags & FLAG_WINDOW_MAXIMIZED)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED))
{
SDL_RestoreWindow(platform.window);
}
- if (flags & FLAG_WINDOW_UNFOCUSED)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
{
//SDL_RaiseWindow(platform.window);
TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL");
}
- if (flags & FLAG_WINDOW_TOPMOST)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
{
SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE);
}
- if (flags & FLAG_WINDOW_ALWAYS_RUN)
- {
- CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN;
- }
- if (flags & FLAG_WINDOW_TRANSPARENT)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
{
TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_SDL");
}
- if (flags & FLAG_WINDOW_HIGHDPI)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
{
// NOTE: There also doesn't seem to be a feature to disable high DPI once enabled
TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_SDL");
}
- if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
{
//SDL_SetWindowGrab(platform.window, SDL_TRUE);
TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_SDL");
}
- if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
+ if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
SDL_SetWindowFullscreen(platform.window, 0);
}
- if (flags & FLAG_MSAA_4X_HINT)
+ if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); // Disable multisampling buffers
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); // Disable multisampling
}
- if (flags & FLAG_INTERLACED_HINT)
+ if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
{
TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_SDL");
}
@@ -838,7 +831,7 @@ void SetWindowMonitor(int monitor)
{
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
- if ((monitor > 0) && (monitor <= monitorCount))
+ if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@@ -847,7 +840,7 @@ void SetWindowMonitor(int monitor)
// 1. SDL started supporting moving exclusive fullscreen windows between displays on SDL3,
// see commit https://github.com/libsdl-org/SDL/commit/3f5ef7dd422057edbcf3e736107e34be4b75d9ba
// 2. A workaround for SDL2 is leaving fullscreen, moving the window, then entering full screen again
- const bool wasFullscreen = ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)? true : false;
+ const bool wasFullscreen = (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))? true : false;
const int screenWidth = CORE.Window.screen.width;
const int screenHeight = CORE.Window.screen.height;
@@ -966,7 +959,7 @@ Vector2 GetMonitorPosition(int monitor)
{
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
- if ((monitor > 0) && (monitor <= monitorCount))
+ if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@@ -994,7 +987,7 @@ int GetMonitorWidth(int monitor)
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
- if ((monitor > 0) && (monitor <= monitorCount))
+ if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@@ -1015,7 +1008,7 @@ int GetMonitorHeight(int monitor)
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
- if ((monitor > 0) && (monitor <= monitorCount))
+ if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@@ -1036,7 +1029,7 @@ int GetMonitorPhysicalWidth(int monitor)
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
- if ((monitor > 0) && (monitor <= monitorCount))
+ if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@@ -1046,7 +1039,7 @@ int GetMonitorPhysicalWidth(int monitor)
SDL_DisplayMode mode;
SDL_GetCurrentDisplayMode(monitor, &mode);
// Calculate size on inches, then convert to millimeter
- if (ddpi > 0.0f) width = (mode.w/ddpi)*25.4f;
+ if (ddpi > 0.0f) width = (int)((mode.w/ddpi)*25.4f);
}
else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
@@ -1060,7 +1053,7 @@ int GetMonitorPhysicalHeight(int monitor)
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
- if ((monitor > 0) && (monitor <= monitorCount))
+ if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@@ -1070,7 +1063,7 @@ int GetMonitorPhysicalHeight(int monitor)
SDL_DisplayMode mode;
SDL_GetCurrentDisplayMode(monitor, &mode);
// Calculate size on inches, then convert to millimeter
- if (ddpi > 0.0f) height = (mode.h/ddpi)*25.4f;
+ if (ddpi > 0.0f) height = (int)((mode.h/ddpi)*25.4f);
}
else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
@@ -1084,7 +1077,7 @@ int GetMonitorRefreshRate(int monitor)
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
- if ((monitor > 0) && (monitor <= monitorCount))
+ if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@@ -1104,7 +1097,7 @@ const char *GetMonitorName(int monitor)
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
- if ((monitor > 0) && (monitor <= monitorCount))
+ if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@@ -1132,14 +1125,15 @@ Vector2 GetWindowScaleDPI(void)
{
Vector2 scale = { 1.0f, 1.0f };
-#ifndef USING_VERSION_SDL3
- // NOTE: SDL_GetWindowDisplayScale was only added on SDL3
- // see https://wiki.libsdl.org/SDL3/SDL_GetWindowDisplayScale
- // TODO: Implement the window scale factor calculation manually
- TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
-#else
+#if defined(USING_VERSION_SDL3)
+ // NOTE: SDL_GetWindowDisplayScale added on SDL3
+ // REF: https://wiki.libsdl.org/SDL3/SDL_GetWindowDisplayScale
scale.x = SDL_GetWindowDisplayScale(platform.window);
scale.y = scale.x;
+#else
+ // NOTE: SDL_GetWindowDisplayScale not available on SDL2
+ // TODO: Implement the window scale factor calculation manually
+ TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
#endif
return scale;
@@ -1193,14 +1187,14 @@ Image GetClipboardImage(void)
size_t dataSize = 0;
void *fileData = NULL;
- for (int i = 0; i < SDL_arraysize(imageFormats); ++i)
+ for (int i = 0; i < SDL_arraysize(imageFormats); i++)
{
// NOTE: This pointer should be free with SDL_free() at some point
fileData = SDL_GetClipboardData(imageFormats[i], &dataSize);
if (fileData)
{
- image = LoadImageFromMemory(imageExtensions[i], fileData, dataSize);
+ image = LoadImageFromMemory(imageExtensions[i], fileData, (int)dataSize);
if (IsImageValid(image))
{
TRACELOG(LOG_INFO, "Clipboard: Got image from clipboard successfully: %s", imageExtensions[i]);
@@ -1242,14 +1236,7 @@ void EnableCursor(void)
{
SDL_SetRelativeMouseMode(SDL_FALSE);
-#if defined(USING_VERSION_SDL3)
- // NOTE: SDL_ShowCursor() has been split into three functions:
- // SDL_ShowCursor(), SDL_HideCursor(), and SDL_CursorVisible()
- SDL_ShowCursor();
-#else
- SDL_ShowCursor(SDL_ENABLE);
-#endif
-
+ ShowCursor();
CORE.Input.Mouse.cursorLocked = false;
}
@@ -1258,6 +1245,7 @@ void DisableCursor(void)
{
SDL_SetRelativeMouseMode(SDL_TRUE);
+ HideCursor();
CORE.Input.Mouse.cursorLocked = true;
}
@@ -1290,7 +1278,7 @@ double GetTime(void)
// 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
-// Ref: https://github.com/raysan5/raylib/issues/686
+// REF: https://github.com/raysan5/raylib/issues/686
void OpenURL(const char *url)
{
// Security check to (partially) avoid malicious code
@@ -1406,11 +1394,11 @@ void PollInputEvents(void)
//-----------------------------------------------------------------------------
// WARNING: Indexes into this array are obtained by using SDL_Scancode values, not SDL_Keycode values
//const Uint8 *keys = SDL_GetKeyboardState(NULL);
- //for (int i = 0; i < 256; ++i) CORE.Input.Keyboard.currentKeyState[i] = keys[i];
+ //for (int i = 0; i < 256; i++) CORE.Input.Keyboard.currentKeyState[i] = keys[i];
CORE.Window.resizedLastFrame = false;
- if ((CORE.Window.eventWaiting) || (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) == 0)))
+ if ((CORE.Window.eventWaiting) || (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED) && !FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN)))
{
SDL_WaitEvent(NULL);
CORE.Time.previous = GetTime();
@@ -1436,11 +1424,13 @@ void PollInputEvents(void)
CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
#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, 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);
+ // 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,
+ // you should make a copy of it. SDL_TEXTINPUTEVENT_TEXT_SIZE is no longer necessary and has been removed
+ strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data, MAX_FILEPATH_LENGTH - 1);
#else
- 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
@@ -1451,9 +1441,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
@@ -1463,13 +1453,13 @@ void PollInputEvents(void)
} break;
- // Window events are also polled (Minimized, maximized, close...)
+ // Window events are also polled (minimized, maximized, close...)
#ifndef USING_VERSION_SDL3
// SDL3 states:
// The SDL_WINDOWEVENT_* events have been moved to top level events, and SDL_WINDOWEVENT has been removed
// In general, handling this change just means checking for the individual events instead of first checking for SDL_WINDOWEVENT
- // and then checking for window events. You can compare the event >= SDL_EVENT_WINDOW_FIRST and <= SDL_EVENT_WINDOW_LAST if you need to see whether it's a window event.
+ // and then checking for window events. You can compare the event >= SDL_EVENT_WINDOW_FIRST and <= SDL_EVENT_WINDOW_LAST if you need to see whether it's a window event
case SDL_WINDOWEVENT:
{
switch (event.window.event)
@@ -1482,7 +1472,7 @@ void PollInputEvents(void)
const int height = event.window.data2;
SetupViewport(width, height);
// if we are doing automatic DPI scaling, then the "screen" size is divided by the window scale
- if (IsWindowState(FLAG_WINDOW_HIGHDPI))
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
{
CORE.Window.screen.width = (int)(width/GetWindowScaleDPI().x);
CORE.Window.screen.height = (int)(height/GetWindowScaleDPI().y);
@@ -1497,8 +1487,9 @@ 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) to remove the FLAG_WINDOW_MAXIMIZED accordingly
- if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0)
+ // 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))
{
int borderTop = 0;
int borderLeft = 0;
@@ -1508,59 +1499,53 @@ void PollInputEvents(void)
SDL_Rect usableBounds;
SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(platform.window), &usableBounds);
- if ((width + borderLeft + borderRight != usableBounds.w) && (height + borderTop + borderBottom != usableBounds.h)) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
+ if ((width + borderLeft + borderRight != usableBounds.w) && (height + borderTop + borderBottom != usableBounds.h)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
}
#endif
} break;
- case SDL_WINDOWEVENT_ENTER:
- {
- CORE.Input.Mouse.cursorOnScreen = true;
- } break;
- case SDL_WINDOWEVENT_LEAVE:
- {
- CORE.Input.Mouse.cursorOnScreen = false;
- } break;
+ case SDL_WINDOWEVENT_ENTER: CORE.Input.Mouse.cursorOnScreen = true; break;
+ case SDL_WINDOWEVENT_LEAVE: CORE.Input.Mouse.cursorOnScreen = false; break;
case SDL_WINDOWEVENT_MINIMIZED:
{
- if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) == 0) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED;
+ if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
} break;
case SDL_WINDOWEVENT_MAXIMIZED:
{
- if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) == 0) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
+ if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
} break;
case SDL_WINDOWEVENT_RESTORED:
{
- if ((SDL_GetWindowFlags(platform.window) & SDL_WINDOW_MINIMIZED) == 0)
+ if (!FLAG_IS_SET(SDL_GetWindowFlags(platform.window), SDL_WINDOW_MINIMIZED))
{
- if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
}
#ifdef USING_VERSION_SDL3
- if ((SDL_GetWindowFlags(platform.window) & SDL_WINDOW_MAXIMIZED) == 0)
+ if (!FLAG_IS_SET(SDL_GetWindowFlags(platform.window), SDL_WINDOW_MAXIMIZED))
{
- if ((CORE.Window.flags & SDL_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~SDL_WINDOW_MAXIMIZED;
+ if (FLAG_IS_SET(CORE.Window.flags, SDL_WINDOW_MAXIMIZED)) FLAG_CLEAR(CORE.Window.flags, SDL_WINDOW_MAXIMIZED);
}
#endif
} break;
case SDL_WINDOWEVENT_HIDDEN:
{
- if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) == 0) CORE.Window.flags |= FLAG_WINDOW_HIDDEN;
+ if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN);
} break;
case SDL_WINDOWEVENT_SHOWN:
{
- if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_HIDDEN);
} break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
{
- if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
} break;
case SDL_WINDOWEVENT_FOCUS_LOST:
{
- if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0) CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
+ if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
} break;
#ifndef USING_VERSION_SDL3
@@ -1573,7 +1558,7 @@ void PollInputEvents(void)
case SDL_KEYDOWN:
{
#if defined(USING_VERSION_SDL3)
- // SDL3 Migration: The following structures have been removed: * SDL_Keysym
+ // SDL3 Migration: The following structures have been removed: SDL_Keysym
KeyboardKey key = ConvertScancodeToKey(event.key.scancode);
#else
KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode);
@@ -1593,11 +1578,9 @@ void PollInputEvents(void)
if (event.key.repeat) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
- // TODO: Put exitKey verification outside the switch?
- if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey])
- {
- CORE.Window.shouldClose = true;
- }
+ // Check for registered exit key to request exit game loop on next iteration
+ if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey]) CORE.Window.shouldClose = true;
+
} break;
case SDL_KEYUP:
@@ -1708,7 +1691,7 @@ void PollInputEvents(void)
int jid = event.jdevice.which; // Joystick device index
// check if already added at InitPlatform
- for (int i = 0; i < MAX_GAMEPADS; ++i)
+ for (int i = 0; i < MAX_GAMEPADS; i++)
{
if (jid == platform.gamepadId[i])
{
@@ -1734,12 +1717,11 @@ void PollInputEvents(void)
CORE.Input.Gamepad.axisState[nextAvailableSlot][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
CORE.Input.Gamepad.axisState[nextAvailableSlot][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
memset(CORE.Input.Gamepad.name[nextAvailableSlot], 0, MAX_GAMEPAD_NAME_LENGTH);
- strncpy(CORE.Input.Gamepad.name[nextAvailableSlot], SDL_GameControllerNameForIndex(nextAvailableSlot), MAX_GAMEPAD_NAME_LENGTH - 1);
- }
- else
- {
- TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());
+ const char *controllerName = SDL_GameControllerNameForIndex(nextAvailableSlot);
+ if (controllerName != NULL) strncpy(CORE.Input.Gamepad.name[nextAvailableSlot], controllerName, MAX_GAMEPAD_NAME_LENGTH - 1);
+ else strncpy(CORE.Input.Gamepad.name[nextAvailableSlot], "noname", 6);
}
+ else TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());
}
} break;
case SDL_JOYDEVICEREMOVED:
@@ -1762,7 +1744,11 @@ void PollInputEvents(void)
{
int button = -1;
+ #if defined(USING_VERSION_SDL3)
switch (event.gbutton.button)
+ #else
+ switch (event.jbutton.button)
+ #endif
{
case SDL_CONTROLLER_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
case SDL_CONTROLLER_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
@@ -1790,7 +1776,11 @@ void PollInputEvents(void)
{
for (int i = 0; i < MAX_GAMEPADS; i++)
{
+ #if defined(USING_VERSION_SDL3)
if (platform.gamepadId[i] == event.gbutton.which)
+ #else
+ if (platform.gamepadId[i] == event.jbutton.which)
+ #endif
{
CORE.Input.Gamepad.currentButtonState[i][button] = 1;
CORE.Input.Gamepad.lastButtonPressed = button;
@@ -1803,7 +1793,11 @@ void PollInputEvents(void)
{
int button = -1;
+ #if defined(USING_VERSION_SDL3)
switch (event.gbutton.button)
+ #else
+ switch (event.jbutton.button)
+ #endif
{
case SDL_CONTROLLER_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
case SDL_CONTROLLER_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
@@ -1831,7 +1825,11 @@ void PollInputEvents(void)
{
for (int i = 0; i < MAX_GAMEPADS; i++)
{
+ #if defined(USING_VERSION_SDL3)
if (platform.gamepadId[i] == event.gbutton.which)
+ #else
+ if (platform.gamepadId[i] == event.jbutton.which)
+ #endif
{
CORE.Input.Gamepad.currentButtonState[i][button] = 0;
if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
@@ -1930,38 +1928,30 @@ int InitPlatform(void)
// Initialize graphic device: display/window and graphic context
//----------------------------------------------------------------------------
unsigned int flags = 0;
- flags |= SDL_WINDOW_SHOWN;
- flags |= SDL_WINDOW_INPUT_FOCUS;
- flags |= SDL_WINDOW_MOUSE_FOCUS;
- flags |= SDL_WINDOW_MOUSE_CAPTURE; // Window has mouse captured
+ FLAG_SET(flags, SDL_WINDOW_SHOWN);
+ FLAG_SET(flags, SDL_WINDOW_INPUT_FOCUS);
+ FLAG_SET(flags, SDL_WINDOW_MOUSE_FOCUS);
+ FLAG_SET(flags, SDL_WINDOW_MOUSE_CAPTURE); // Window has mouse captured
// Check window creation flags
- if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)
+ 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);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) FLAG_SET(flags, SDL_WINDOW_RESIZABLE);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_SET(flags, SDL_WINDOW_MINIMIZED);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_SET(flags, SDL_WINDOW_MAXIMIZED);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED))
{
- CORE.Window.fullscreen = true;
- flags |= SDL_WINDOW_FULLSCREEN;
+ FLAG_CLEAR(flags, SDL_WINDOW_INPUT_FOCUS);
+ FLAG_CLEAR(flags, SDL_WINDOW_MOUSE_FOCUS);
}
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TOPMOST)) FLAG_SET(flags, SDL_WINDOW_ALWAYS_ON_TOP);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MOUSE_PASSTHROUGH)) FLAG_CLEAR(flags, SDL_WINDOW_MOUSE_CAPTURE);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) FLAG_SET(flags, SDL_WINDOW_ALLOW_HIGHDPI);
- //if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) == 0) flags |= SDL_WINDOW_HIDDEN;
- if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) flags |= SDL_WINDOW_BORDERLESS;
- if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) flags |= SDL_WINDOW_RESIZABLE;
- if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) flags |= SDL_WINDOW_MINIMIZED;
- if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) flags |= SDL_WINDOW_MAXIMIZED;
-
- if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0)
- {
- flags &= ~SDL_WINDOW_INPUT_FOCUS;
- flags &= ~SDL_WINDOW_MOUSE_FOCUS;
- }
-
- if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) flags |= SDL_WINDOW_ALWAYS_ON_TOP;
- if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
-
- if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) flags |= SDL_WINDOW_ALLOW_HIGHDPI;
-
- //if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) flags |= SDL_WINDOW_TRANSPARENT; // Alternative: SDL_GL_ALPHA_SIZE = 8
-
- //if ((CORE.Window.flags & FLAG_FULLSCREEN_DESKTOP) > 0) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
+ //if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TRANSPARENT)) FLAG_SET(flags, SDL_WINDOW_TRANSPARENT); // Alternative: SDL_GL_ALPHA_SIZE = 8
+ //if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_DESKTOP)) FLAG_SET(flags, SDL_WINDOW_FULLSCREEN_DESKTOP);
// NOTE: Some OpenGL context attributes must be set before window creation
@@ -2004,7 +1994,7 @@ int InitPlatform(void)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
}
- if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
@@ -2047,7 +2037,7 @@ int InitPlatform(void)
if (platform.glContext != NULL)
{
- SDL_GL_SetSwapInterval((CORE.Window.flags & FLAG_VSYNC_HINT)? 1 : 0);
+ SDL_GL_SetSwapInterval((FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT))? 1: 0);
// Load OpenGL extensions
// NOTE: GL procedures address loader is required to load extensions
@@ -2070,33 +2060,28 @@ int InitPlatform(void)
platform.gamepadId[i] = -1; // Set all gamepad initial instance ids as invalid to not conflict with instance id zero
}
- int numJoysticks = 0;
- SDL_JoystickID *joysticks = SDL_GetJoysticks(&numJoysticks); // array of joystick IDs, they do not start from 0
+ int numJoysticks = SDL_NumJoysticks();
- if (joysticks)
+ for (int i = 0; (i < numJoysticks) && (i < MAX_GAMEPADS); i++)
{
- for (int i = 0; (i < numJoysticks) && (i < MAX_GAMEPADS); i++)
- {
- platform.gamepad[i] = SDL_GameControllerOpen(joysticks[i]);
- platform.gamepadId[i] = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(platform.gamepad[i]));
+ platform.gamepad[i] = SDL_GameControllerOpen(i);
+ platform.gamepadId[i] = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(platform.gamepad[i]));
- if (platform.gamepad[i])
- {
- CORE.Input.Gamepad.ready[i] = true;
- CORE.Input.Gamepad.axisCount[i] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[i]));
- CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
- CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
- strncpy(CORE.Input.Gamepad.name[i], SDL_GameControllerNameForIndex(i), MAX_GAMEPAD_NAME_LENGTH - 1);
- CORE.Input.Gamepad.name[i][MAX_GAMEPAD_NAME_LENGTH - 1] = '\0';
- }
- else TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());
+ if (platform.gamepad[i])
+ {
+ CORE.Input.Gamepad.ready[i] = true;
+ CORE.Input.Gamepad.axisCount[i] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[i]));
+ CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
+ CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
+ strncpy(CORE.Input.Gamepad.name[i], SDL_GameControllerNameForIndex(i), MAX_GAMEPAD_NAME_LENGTH - 1);
+ CORE.Input.Gamepad.name[i][MAX_GAMEPAD_NAME_LENGTH - 1] = '\0';
}
- SDL_free(joysticks);
+ else TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());
}
// Disable mouse events being interpreted as touch events
// NOTE: This is wanted because there are SDL_FINGER* events available which provide unique data
- // Due to the way PollInputEvents() and rgestures.h are currently implemented, setting this won't break SUPPORT_MOUSE_GESTURES
+ // Due to the way PollInputEvents() and rgestures.h are currently implemented, setting this won't break SUPPORT_MOUSE_GESTURES
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
@@ -2212,7 +2197,7 @@ static void UpdateTouchPointsSDL(SDL_TouchFingerEvent event)
for (int i = 0; i < CORE.Input.Touch.pointCount; i++)
{
SDL_Finger *finger = SDL_GetTouchFinger(event.touchId, i);
- CORE.Input.Touch.pointId[i] = finger->id;
+ CORE.Input.Touch.pointId[i] = (int)finger->id;
CORE.Input.Touch.position[i].x = finger->x*CORE.Window.screen.width;
CORE.Input.Touch.position[i].y = finger->y*CORE.Window.screen.height;
CORE.Input.Touch.currentTouchState[i] = 1;
diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c
index 1dadd5586..973fafa68 100644
--- a/src/platforms/rcore_desktop_win32.c
+++ b/src/platforms/rcore_desktop_win32.c
@@ -72,6 +72,8 @@
#include
#include
+#include // Required for alloca()
+
#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
#include
#endif
@@ -195,8 +197,8 @@ static PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = NULL;
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
-#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004
-#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
+#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004
+#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
//----------------------------------------------------------------------------------
// Types and Structures Definition
@@ -260,9 +262,9 @@ static bool DecoratedFromStyle(DWORD style)
// Get window style from required flags
static DWORD MakeWindowStyle(unsigned flags)
{
- // We don't need this since we don't have any child windows, but I guess
- // it improves efficiency, plus, windows adds this flag automatically anyway
- // so it keeps our flags in sync with the OS
+ // Flag is not needed because there are no child windows,
+ // 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;
style |= (flags & FLAG_WINDOW_HIDDEN)? 0 : WS_VISIBLE;
@@ -271,7 +273,7 @@ static DWORD MakeWindowStyle(unsigned flags)
// Minimized takes precedence over maximized
int mized = MIZED_NONE;
- if (FLAG_CHECK(flags, FLAG_WINDOW_MINIMIZED)) mized = MIZED_MIN;
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED)) mized = MIZED_MIN;
if (flags & FLAG_WINDOW_MAXIMIZED) mized = MIZED_MAX;
switch (mized)
@@ -937,7 +939,7 @@ void SetWindowIcon(Image image)
// Set icon for window
void SetWindowIcons(Image *images, int count)
{
- // TODO.
+ // TODO: Implement SetWindowIcons()
}
void SetWindowTitle(const char *title)
@@ -1228,7 +1230,7 @@ void SwapScreenBuffer(void)
// Get elapsed time measure in seconds
double GetTime(void)
{
- LARGE_INTEGER now;
+ LARGE_INTEGER now = 0;
QueryPerformanceCounter(&now);
return (double)(now.QuadPart - CORE.Time.base)/(double)platform.timerFrequency.QuadPart;
}
@@ -1237,14 +1239,18 @@ double GetTime(void)
// 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
-// Ref: https://github.com/raysan5/raylib/issues/686
+// REF: https://github.com/raysan5/raylib/issues/686
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
{
- TRACELOG(LOG_WARNING, "OpenURL not implemented");
+ char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char));
+ sprintf(cmd, "explorer \"%s\"", url);
+ int result = system(cmd);
+ if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
+ RL_FREE(cmd);
}
}
@@ -1871,13 +1877,23 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara
} break;
case WM_DPICHANGED:
{
+ // Get current dpi scale factor
+ float scalex = HIWORD(wParam)/96.0f;
+ float scaley = LOWORD(wParam)/96.0f;
+
RECT *suggestedRect = (RECT *)lparam;
// Never set the window size to anything other than the suggested rect here
// Doing so can cause a window to stutter between monitors when transitioning between them
- int result = (int)SetWindowPos(hwnd, NULL, suggestedRect->left, suggestedRect->top,
- suggestedRect->right - suggestedRect->left, suggestedRect->bottom - suggestedRect->top, SWP_NOZORDER | SWP_NOACTIVATE);
+ int result = (int)SetWindowPos(hwnd, NULL,
+ suggestedRect->left, suggestedRect->top,
+ suggestedRect->right - suggestedRect->left,
+ suggestedRect->bottom - suggestedRect->top,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+
if (result == 0) TRACELOG(LOG_ERROR, "Failed to set window position [ERROR: %lu]", GetLastError());
+
+ // TODO: Update screen data, render size, screen scaling, viewport...
} break;
case WM_SETCURSOR:
@@ -1981,7 +1997,7 @@ static void HandleKey(WPARAM wparam, LPARAM lparam, char state)
{
CORE.Input.Keyboard.currentKeyState[key] = state;
- if ((key == KEY_ESCAPE) && (state == 1)) CORE.Window.shouldClose = 1;
+ if ((key == KEY_ESCAPE) && (state == 1)) CORE.Window.shouldClose = true;
}
else TRACELOG(LOG_WARNING, "INPUT: Unknown (or currently unhandled) virtual keycode %d (0x%x)", wparam, wparam);
@@ -2033,8 +2049,7 @@ static void HandleWindowResize(HWND hwnd, int *width, int *height)
// TODO: Update framebuffer on resize
CORE.Window.currentFbo.width = (int)clientSize.cx;
CORE.Window.currentFbo.height = (int)clientSize.cy;
- //glViewport(0, 0, clientSize.cx, clientSize.cy);
- //SetupFramebuffer(0, 0);
+ //SetupViewport(0, 0, clientSize.cx, clientSize.cy);
SetupViewport(clientSize.cx, clientSize.cy);
CORE.Window.resizedLastFrame = true;
diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c
index f9f35676c..5f317ef6a 100644
--- a/src/platforms/rcore_drm.c
+++ b/src/platforms/rcore_drm.c
@@ -224,7 +224,7 @@ static const short linuxToRaylibMap[KEYMAP_SIZE] = {
248, 0, 0, 0, 0, 0, 0, 0,
// Gamepads are mapped according to:
- // https://www.kernel.org/doc/html/next/input/gamepad.html
+ // REF: https://www.kernel.org/doc/html/next/input/gamepad.html
// Those mappings are standardized, but that doesn't mean people follow
// the standards, so this is more of an approximation
[BTN_DPAD_UP] = GAMEPAD_BUTTON_LEFT_FACE_UP,
@@ -269,6 +269,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
//----------------------------------------------------------------------------------
@@ -641,7 +643,7 @@ static uint32_t GetOrCreateFbForBo(struct gbm_bo *bo)
}
// Renders a blank frame to allocate initial buffers
-// TODO: WARNING: Platform layers do not include OpenGL code!
+// TODO: WARNING: Platform backend should not include OpenGL code
void RenderBlankFrame()
{
glClearColor(0, 0, 0, 1);
@@ -828,15 +830,6 @@ void SwapScreenBuffer(void)
return;
}
- // Get the software rendered color buffer
- int bufferWidth = 0, bufferHeight = 0;
- void *colorBuffer = swGetColorBuffer(&bufferWidth, &bufferHeight);
- if (!colorBuffer)
- {
- TRACELOG(LOG_ERROR, "DISPLAY: Failed to get software color buffer");
- return;
- }
-
// Retrieving the dimensions of the display mode used
drmModeModeInfo *mode = &platform.connector->modes[platform.modeIndex];
uint32_t width = mode->hdisplay;
@@ -904,16 +897,8 @@ void SwapScreenBuffer(void)
}
// Copy the software rendered buffer to the dumb buffer with scaling if needed
- if (bufferWidth == width && bufferHeight == height)
- {
- // Direct copy if sizes match
- swCopyFramebuffer(0, 0, bufferWidth, bufferHeight, SW_RGBA, SW_UNSIGNED_BYTE, dumbBuffer);
- }
- else
- {
- // Scale the software buffer to match the display mode
- swBlitFramebuffer(0, 0, width, height, 0, 0, bufferWidth, bufferHeight, SW_RGBA, SW_UNSIGNED_BYTE, dumbBuffer);
- }
+ // NOTE: RLSW will make a simple copy if the dimensions match
+ swBlitFramebuffer(0, 0, width, height, 0, 0, width, height, SW_RGBA, SW_UNSIGNED_BYTE, dumbBuffer);
// Unmap the buffer
munmap(dumbBuffer, creq.size);
@@ -1034,7 +1019,7 @@ double GetTime(void)
// 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
-// Ref: https://github.com/raysan5/raylib/issues/686
+// REF: https://github.com/raysan5/raylib/issues/686
void OpenURL(const char *url)
{
TRACELOG(LOG_WARNING, "OpenURL() not implemented on target platform");
@@ -1165,8 +1150,7 @@ int InitPlatform(void)
// Initialize graphic device: display/window and graphic context
//----------------------------------------------------------------------------
- CORE.Window.fullscreen = true;
- CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
+ FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
#if defined(DEFAULT_GRAPHIC_DEVICE_DRM)
platform.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR);
@@ -1231,9 +1215,9 @@ int InitPlatform(void)
TRACELOG(LOG_TRACE, "DISPLAY: Connector %i modes detected: %i", i, con->count_modes);
TRACELOG(LOG_TRACE, "DISPLAY: Connector %i status: %s", i,
- (con->connection == DRM_MODE_CONNECTED) ? "CONNECTED" :
- (con->connection == DRM_MODE_DISCONNECTED) ? "DISCONNECTED" :
- (con->connection == DRM_MODE_UNKNOWNCONNECTION) ? "UNKNOWN" : "OTHER");
+ (con->connection == DRM_MODE_CONNECTED)? "CONNECTED" :
+ (con->connection == DRM_MODE_DISCONNECTED)? "DISCONNECTED" :
+ (con->connection == DRM_MODE_UNKNOWNCONNECTION)? "UNKNOWN" : "OTHER");
// In certain cases the status of the conneciton is reported as UKNOWN, but it is still connected
// This might be a hardware or software limitation like on Raspberry Pi Zero with composite output
@@ -1315,8 +1299,8 @@ int InitPlatform(void)
CORE.Window.screen.height = CORE.Window.display.height;
}
- const bool allowInterlaced = CORE.Window.flags & FLAG_INTERLACED_HINT;
- const int fps = (CORE.Time.target > 0) ? (1.0/CORE.Time.target) : 60;
+ const bool allowInterlaced = FLAG_IS_SET(CORE.Window.flags, FLAG_INTERLACED_HINT);
+ const int fps = (CORE.Time.target > 0)? (1.0/CORE.Time.target) : 60;
// Try to find an exact matching mode
platform.modeIndex = FindExactConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
@@ -1346,7 +1330,7 @@ int InitPlatform(void)
TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", platform.connector->modes[platform.modeIndex].name,
platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay,
- (platform.connector->modes[platform.modeIndex].flags & DRM_MODE_FLAG_INTERLACE) ? 'i' : 'p',
+ FLAG_IS_SET(platform.connector->modes[platform.modeIndex].flags, DRM_MODE_FLAG_INTERLACE)? 'i' : 'p',
platform.connector->modes[platform.modeIndex].vrefresh);
drmModeFreeEncoder(enc);
@@ -1363,7 +1347,7 @@ int InitPlatform(void)
platform.connector->modes[0].name,
platform.connector->modes[0].hdisplay,
platform.connector->modes[0].vdisplay,
- (platform.connector->modes[0].flags & DRM_MODE_FLAG_INTERLACE) ? 'i' : 'p',
+ (platform.connector->modes[0].flags & DRM_MODE_FLAG_INTERLACE)? 'i' : 'p',
platform.connector->modes[0].vrefresh);
}
else
@@ -1402,7 +1386,7 @@ int InitPlatform(void)
EGLint samples = 0;
EGLint sampleBuffer = 0;
- if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
{
samples = 4;
sampleBuffer = 1;
@@ -1474,7 +1458,7 @@ int InitPlatform(void)
// find the EGL config that matches the previously setup GBM format
int found = 0;
- for (EGLint i = 0; i < matchingNumConfigs; ++i)
+ for (EGLint i = 0; i < matchingNumConfigs; i++)
{
EGLint id = 0;
if (!eglGetConfigAttrib(platform.device, configs[i], EGL_NATIVE_VISUAL_ID, &id))
@@ -1579,17 +1563,17 @@ int InitPlatform(void)
TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
#endif
- if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
+ 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 SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2);
// Set some default window flags
- CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false
- CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false
- CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true
- CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_HIDDEN); // false
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // false
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); // true
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // false
//----------------------------------------------------------------------------
// Initialize timing system
@@ -1738,8 +1722,8 @@ static void InitKeyboard(void)
// New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing
// NOTE: ISIG controls if ^C and ^Z generate break signals or not
- keyboardNewSettings.c_lflag &= ~(ICANON | ECHO | ISIG);
- //keyboardNewSettings.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF);
+ FLAG_CLEAR(keyboardNewSettings.c_lflag, ICANON | ECHO | ISIG);
+ //FLAG_CLEAR(keyboardNewSettings.c_iflag, ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF);
keyboardNewSettings.c_cc[VMIN] = 1;
keyboardNewSettings.c_cc[VTIME] = 0;
@@ -1758,10 +1742,10 @@ static void InitKeyboard(void)
else
{
// Reconfigure keyboard mode to get:
- // - scancodes (K_RAW)
- // - keycodes (K_MEDIUMRAW)
- // - ASCII chars (K_XLATE)
- // - UNICODE chars (K_UNICODE)
+ // - scancodes (K_RAW)
+ // - keycodes (K_MEDIUMRAW)
+ // - ASCII chars (K_XLATE)
+ // - UNICODE chars (K_UNICODE)
ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE); // ASCII chars
}
@@ -1883,7 +1867,7 @@ static void ProcessKeyboard(void)
}
#endif // SUPPORT_SSH_KEYBOARD_RPI
-// Initialise user input from evdev(/dev/input/event)
+// Initialize user input from evdev(/dev/input/event)
// this means mouse, keyboard or gamepad devices
static void InitEvdevInput(void)
{
@@ -1891,12 +1875,12 @@ static void InitEvdevInput(void)
DIR *directory = NULL;
struct dirent *entity = NULL;
- // Initialise keyboard file descriptor
+ // Initialize keyboard file descriptor
platform.keyboardFd = -1;
platform.mouseFd = -1;
// Reset variables
- for (int i = 0; i < MAX_TOUCH_POINTS; ++i)
+ for (int i = 0; i < MAX_TOUCH_POINTS; i++)
{
CORE.Input.Touch.position[i].x = -1;
CORE.Input.Touch.position[i].y = -1;
@@ -2578,7 +2562,7 @@ static int FindMatchingConnectorMode(const drmModeConnector *connector, const dr
for (size_t i = 0; i < connector->count_modes; i++)
{
TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, connector->modes[i].hdisplay, connector->modes[i].vdisplay,
- connector->modes[i].vrefresh, (connector->modes[i].flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive");
+ connector->modes[i].vrefresh, (FLAG_IS_SET(connector->modes[i].flags, DRM_MODE_FLAG_INTERLACE) > 0)? "interlaced" : "progressive");
if (0 == BINCMP(&platform.crtc->mode, &platform.connector->modes[i])) return i;
}
@@ -2599,9 +2583,9 @@ static int FindExactConnectorMode(const drmModeConnector *connector, uint width,
{
const drmModeModeInfo *const mode = &platform.connector->modes[i];
- TRACELOG(LOG_TRACE, "DISPLAY: DRM Mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive");
+ TRACELOG(LOG_TRACE, "DISPLAY: DRM Mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (FLAG_IS_SET(mode->flags, DRM_MODE_FLAG_INTERLACE) > 0)? "interlaced" : "progressive");
- if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && !allowInterlaced) continue;
+ if ((FLAG_IS_SET(mode->flags, DRM_MODE_FLAG_INTERLACE) > 0) && !allowInterlaced) continue;
if ((mode->hdisplay == width) && (mode->vdisplay == height) && (mode->vrefresh == fps)) return i;
}
@@ -2625,7 +2609,7 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt
const drmModeModeInfo *const mode = &platform.connector->modes[i];
TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh,
- (mode->flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive");
+ (FLAG_IS_SET(mode->flags, DRM_MODE_FLAG_INTERLACE) > 0)? "interlaced" : "progressive");
if ((mode->hdisplay < width) || (mode->vdisplay < height))
{
@@ -2633,13 +2617,13 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt
continue;
}
- if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && !allowInterlaced)
+ if ((FLAG_IS_SET(mode->flags, DRM_MODE_FLAG_INTERLACE) > 0) && !allowInterlaced)
{
TRACELOG(LOG_TRACE, "DISPLAY: DRM shouldn't choose an interlaced mode");
continue;
}
- const int unusedPixels = (mode->hdisplay - width) * (mode->vdisplay - height);
+ const int unusedPixels = (mode->hdisplay - width)*(mode->vdisplay - height);
const int fpsDiff = mode->vrefresh - fps;
if ((unusedPixels < minUnusedPixels) ||
@@ -2655,4 +2639,82 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt
return nearestIndex;
}
+// Compute framebuffer size relative to screen size and display size
+// NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified
+static void SetupFramebuffer(int width, int height)
+{
+ // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var)
+ if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height))
+ {
+ TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
+
+ // Downscaling to fit display with border-bars
+ float widthRatio = (float)CORE.Window.display.width/(float)CORE.Window.screen.width;
+ float heightRatio = (float)CORE.Window.display.height/(float)CORE.Window.screen.height;
+
+ if (widthRatio <= heightRatio)
+ {
+ CORE.Window.render.width = CORE.Window.display.width;
+ CORE.Window.render.height = (int)round((float)CORE.Window.screen.height*widthRatio);
+ CORE.Window.renderOffset.x = 0;
+ CORE.Window.renderOffset.y = (CORE.Window.display.height - CORE.Window.render.height);
+ }
+ else
+ {
+ CORE.Window.render.width = (int)round((float)CORE.Window.screen.width*heightRatio);
+ CORE.Window.render.height = CORE.Window.display.height;
+ CORE.Window.renderOffset.x = (CORE.Window.display.width - CORE.Window.render.width);
+ CORE.Window.renderOffset.y = 0;
+ }
+
+ // Screen scaling required
+ float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width;
+ CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f);
+
+ // NOTE: We render to full display resolution!
+ // We just need to calculate above parameters for downscale matrix and offsets
+ CORE.Window.render.width = CORE.Window.display.width;
+ CORE.Window.render.height = CORE.Window.display.height;
+
+ TRACELOG(LOG_WARNING, "DISPLAY: Downscale matrix generated, content will be rendered at (%ix%i)", CORE.Window.render.width, CORE.Window.render.height);
+ }
+ else if ((CORE.Window.screen.width < CORE.Window.display.width) || (CORE.Window.screen.height < CORE.Window.display.height))
+ {
+ // Required screen size is smaller than display size
+ TRACELOG(LOG_INFO, "DISPLAY: Upscaling required: Screen size (%ix%i) smaller than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
+
+ if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0))
+ {
+ CORE.Window.screen.width = CORE.Window.display.width;
+ CORE.Window.screen.height = CORE.Window.display.height;
+ }
+
+ // Upscaling to fit display with border-bars
+ float displayRatio = (float)CORE.Window.display.width/(float)CORE.Window.display.height;
+ float screenRatio = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
+
+ if (displayRatio <= screenRatio)
+ {
+ CORE.Window.render.width = CORE.Window.screen.width;
+ CORE.Window.render.height = (int)round((float)CORE.Window.screen.width/displayRatio);
+ CORE.Window.renderOffset.x = 0;
+ CORE.Window.renderOffset.y = (CORE.Window.render.height - CORE.Window.screen.height);
+ }
+ else
+ {
+ CORE.Window.render.width = (int)round((float)CORE.Window.screen.height*displayRatio);
+ CORE.Window.render.height = CORE.Window.screen.height;
+ CORE.Window.renderOffset.x = (CORE.Window.render.width - CORE.Window.screen.width);
+ CORE.Window.renderOffset.y = 0;
+ }
+ }
+ else
+ {
+ CORE.Window.render.width = CORE.Window.screen.width;
+ CORE.Window.render.height = CORE.Window.screen.height;
+ CORE.Window.renderOffset.x = 0;
+ CORE.Window.renderOffset.y = 0;
+ }
+}
+
// EOF
diff --git a/src/platforms/rcore_memory.c b/src/platforms/rcore_memory.c
new file mode 100644
index 000000000..1b7a55fd8
--- /dev/null
+++ b/src/platforms/rcore_memory.c
@@ -0,0 +1,594 @@
+/**********************************************************************************************
+*
+* rcore_memory - Functions to manage window, graphics device and inputs
+*
+* PLATFORM: MEMORY (No OS)
+* - Memory framebuffer output (no os)
+*
+* LIMITATIONS:
+* - Software renderer (rlsw)
+* - No input system
+*
+* POSSIBLE IMPROVEMENTS:
+* - 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-
+*
+* DEPENDENCIES:
+* - rlsw: Software renderer
+* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
+*
+*
+* LICENSE: zlib/libpng
+*
+* Copyright (c) 2025 Ramon Santamaria (@raysan5) and contributors
+*
+* This software is provided "as-is", without any express or implied warranty. In no event
+* will the authors be held liable for any damages arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose, including commercial
+* applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not claim that you
+* wrote the original software. If you use this software in a product, an acknowledgment
+* in the product documentation would be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
+* as being the original software.
+*
+* 3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#if defined(_WIN32)
+ #include // Required for: kbhit()
+#else
+ // Provide kbhit() function in non-Windows platforms
+ #include
+ #include
+ #include
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Platform-specific required data for timming (Win32)
+#if defined(_WIN32)
+typedef struct _LARGE_INTEGER { int64_t QuadPart; } LARGE_INTEGER;
+__declspec(dllimport) int __stdcall QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount);
+__declspec(dllimport) int __stdcall QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
+#endif
+
+typedef struct {
+ unsigned int *pixels; // Pointer to pixel data buffer (RGBA8888 format)
+#if defined(_WIN32)
+ LARGE_INTEGER timerFrequency;
+#endif
+} PlatformData;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+extern CoreData CORE; // Global CORE state context
+
+static PlatformData platform = { 0 }; // Platform specific data
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+int InitPlatform(void); // Initialize platform (graphics, inputs and more)
+bool InitGraphicsDevice(void); // Initialize graphics device
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+// NOTE: Functions declaration is provided by raylib.h
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+#if !defined(_WIN32)
+static int kbhit(void); // Check if a key has been pressed
+static char getch(void) { return getchar(); } // Get pressed character
+#endif
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Window and Graphics Device
+//----------------------------------------------------------------------------------
+
+// Check if application should close
+bool WindowShouldClose(void)
+{
+ if (CORE.Window.ready) return CORE.Window.shouldClose;
+ else return true;
+}
+
+// Toggle fullscreen mode
+void ToggleFullscreen(void)
+{
+ TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform");
+}
+
+// Toggle borderless windowed mode
+void ToggleBorderlessWindowed(void)
+{
+ TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform");
+}
+
+// Set window state: maximized, if resizable
+void MaximizeWindow(void)
+{
+ TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform");
+}
+
+// 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)
+{
+ TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform");
+}
+
+// Set window configuration state using flags
+void SetWindowState(unsigned int flags)
+{
+ TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform");
+}
+
+// Clear window configuration state flags
+void ClearWindowState(unsigned int flags)
+{
+ TRACELOG(LOG_WARNING, "ClearWindowState() 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
+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;
+}
+
+// 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;
+}
+
+// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
+void SetWindowMaxSize(int width, int height)
+{
+ CORE.Window.screenMax.width = width;
+ CORE.Window.screenMax.height = height;
+}
+
+// Set window dimensions
+void SetWindowSize(int width, int height)
+{
+ TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform");
+}
+
+// Set window opacity, value opacity is between 0.0 and 1.0
+void SetWindowOpacity(float opacity)
+{
+ TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform");
+}
+
+// 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)
+{
+ TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform");
+ return 0;
+}
+
+// Get selected monitor height (currently used by monitor)
+int GetMonitorHeight(int monitor)
+{
+ TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform");
+ return 0;
+}
+
+// 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)
+{
+ TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform");
+ return (Vector2){ 0, 0 };
+}
+
+// Get window scale DPI factor for current monitor
+Vector2 GetWindowScaleDPI(void)
+{
+ TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
+ return (Vector2){ 1.0f, 1.0f };
+}
+
+// Set clipboard text content
+void SetClipboardText(const char *text)
+{
+ TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform");
+}
+
+// Get clipboard text content
+// NOTE: returned string is allocated and freed by GLFW
+const char *GetClipboardText(void)
+{
+ TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform");
+ return NULL;
+}
+
+// Get clipboard image
+Image GetClipboardImage(void)
+{
+ Image image = { 0 };
+
+ TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform");
+
+ return image;
+}
+
+// Show mouse cursor
+void ShowCursor(void)
+{
+ CORE.Input.Mouse.cursorHidden = false;
+}
+
+// Hides mouse cursor
+void HideCursor(void)
+{
+ CORE.Input.Mouse.cursorHidden = true;
+}
+
+// Enables cursor (unlock cursor)
+void EnableCursor(void)
+{
+ // Set cursor position in the middle
+ SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
+
+ CORE.Input.Mouse.cursorHidden = false;
+}
+
+// Disables cursor (lock cursor)
+void DisableCursor(void)
+{
+ // Set cursor position in the middle
+ SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
+
+ CORE.Input.Mouse.cursorHidden = true;
+}
+
+// Swap back buffer with front buffer (screen drawing)
+void SwapScreenBuffer(void)
+{
+ // Update framebuffer
+ rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, platform.pixels);
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Misc
+//----------------------------------------------------------------------------------
+
+// Get elapsed time measure in seconds since InitTimer()
+double GetTime(void)
+{
+ double time = 0.0;
+#if defined(_WIN32)
+ LARGE_INTEGER now = { 0 };
+ QueryPerformanceCounter(&now);
+ return (double)(now.QuadPart - CORE.Time.base)/(double)platform.timerFrequency.QuadPart;
+#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
+ 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()
+#endif
+ 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.
+// REF: https://github.com/raysan5/raylib/issues/686
+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
+ {
+ char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char));
+ sprintf(cmd, "explorer \"%s\"", url);
+ int result = system(cmd);
+ if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
+ RL_FREE(cmd);
+ }
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Inputs
+//----------------------------------------------------------------------------------
+
+// Set internal gamepad mappings
+int SetGamepadMappings(const char *mappings)
+{
+ TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform");
+ return 0;
+}
+
+// Set gamepad vibration
+void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
+{
+ TRACELOG(LOG_WARNING, "SetGamepadVibration() not implemented on target platform");
+}
+
+// Set mouse position XY
+void SetMousePosition(int x, int y)
+{
+ CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
+ CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
+}
+
+// Set mouse cursor
+void SetMouseCursor(int cursor)
+{
+ TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
+}
+
+// Get physical key name
+const char *GetKeyName(int 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 key repeats
+ for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
+
+ // Reset last gamepad button/axis registered state
+ CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
+ //CORE.Input.Gamepad.axisCount = 0;
+
+ // 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 };
+
+ // Register previous keys states
+ // NOTE: Android supports up to 260 keys
+ for (int i = 0; i < 260; i++)
+ {
+ CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
+ CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
+ }
+
+ // TODO: Poll input events for current platform
+
+ // Check for key pressed to exit
+ if (kbhit())
+ {
+ int key = getch();
+ if (key == 27) CORE.Window.shouldClose = true; // KEY_SCAPE
+ }
+}
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+
+// Initialize platform: graphics, inputs and more
+int InitPlatform(void)
+{
+ // Memory framebuffer can only work with software renderer
+ if (rlGetVersion() != RL_OPENGL_11_SOFTWARE)
+ {
+ TRACELOG(LOG_WARNING, "DISPLAY: Memory platform requires software renderer (GRAPHICS_API_OPENGL_11_SOFTWARE)");
+ TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
+ return -1;
+ }
+ else
+ {
+ // Load memory framebuffer with desired screen size
+ platform.pixels = (unsigned int *)RL_CALLOC(CORE.Window.screen.width*CORE.Window.screen.height, sizeof(int));
+ }
+ //----------------------------------------------------------------------------
+
+ // If everything work as expected, we can continue
+ CORE.Window.render.width = CORE.Window.screen.width;
+ CORE.Window.render.height = CORE.Window.screen.height;
+ CORE.Window.currentFbo.width = CORE.Window.render.width;
+ CORE.Window.currentFbo.height = CORE.Window.render.height;
+
+ 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);
+
+ CORE.Window.ready = true;
+
+ // TODO: Load OpenGL extensions
+ // NOTE: GL procedures address loader is required to load extensions
+ //----------------------------------------------------------------------------
+ // ...
+ //----------------------------------------------------------------------------
+
+ // TODO: Initialize input events system
+ // It could imply keyboard, mouse, gamepad, touch...
+ // Depending on the platform libraries/SDK it could use a callback mechanism
+ // For system events and inputs evens polling on a per-frame basis, use PollInputEvents()
+ //----------------------------------------------------------------------------
+ // ...
+ //----------------------------------------------------------------------------
+
+ // Initialize timing system
+ //----------------------------------------------------------------------------
+#if defined(_WIN32)
+ LARGE_INTEGER time = { 0 };
+ QueryPerformanceCounter(&time);
+ QueryPerformanceFrequency(&platform.timerFrequency);
+ CORE.Time.base = time.QuadPart;
+#endif
+ InitTimer();
+ //----------------------------------------------------------------------------
+
+ // Initialize storage system
+ //----------------------------------------------------------------------------
+ CORE.Storage.basePath = GetWorkingDirectory();
+ //----------------------------------------------------------------------------
+
+ TRACELOG(LOG_INFO, "PLATFORM: MEMORY: Initialized successfully");
+
+ return 0;
+}
+
+// Close platform
+void ClosePlatform(void)
+{
+ RL_FREE(platform.pixels);
+}
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+#if !defined(_WIN32)
+// Check if a key has been pressed
+static int kbhit(void)
+{
+ struct termios oldt = { 0 };
+ struct termios newt = { 0 };
+ int ch = 0;
+ int oldf = 0;
+
+ tcgetattr(STDIN_FILENO, &oldt);
+ newt = oldt;
+ newt.c_lflag &= ~(ICANON | ECHO);
+ tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+ oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
+ fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
+
+ ch = getchar();
+
+ tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
+ fcntl(STDIN_FILENO, F_SETFL, oldf);
+
+ if (ch != EOF)
+ {
+ ungetc(ch, stdin);
+ return 1;
+ }
+
+ return 0;
+}
+#endif
+
+// EOF
diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c
index 36629fc69..b22d3f2f5 100644
--- a/src/platforms/rcore_template.c
+++ b/src/platforms/rcore_template.c
@@ -54,11 +54,6 @@
typedef struct {
// TODO: Define the platform specific variables required
- // Display data
- EGLDisplay device; // Native display device (physical screen connection)
- EGLSurface surface; // Surface to draw on, framebuffers (connected to context)
- EGLContext context; // Graphic context, mode in which drawing can be done
- EGLConfig config; // Graphic config
} PlatformData;
//----------------------------------------------------------------------------------
@@ -346,10 +341,10 @@ void SwapScreenBuffer(void)
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()
return time;
@@ -366,7 +361,7 @@ void OpenURL(const char *url)
if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
else
{
- // TODO:
+ // TODO: Load url using default browser
}
}
@@ -459,89 +454,20 @@ 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;
- CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
+ FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
- EGLint samples = 0;
- EGLint sampleBuffer = 0;
- if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
{
- samples = 4;
- sampleBuffer = 1;
+ // TODO: Enable MSAA
+
TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
}
- const EGLint framebufferAttribs[] =
- {
- EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support
- EGL_RED_SIZE, 8, // RED color bit depth (alternative: 5)
- EGL_GREEN_SIZE, 8, // GREEN color bit depth (alternative: 6)
- EGL_BLUE_SIZE, 8, // BLUE color bit depth (alternative: 5)
- //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
- EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!)
- //EGL_STENCIL_SIZE, 8, // Stencil buffer size
- EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA
- EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs)
- EGL_NONE
- };
+ // TODO: Init display and graphic device
- const EGLint contextAttribs[] =
- {
- EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL_NONE
- };
-
- EGLint numConfigs = 0;
-
- // 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");
- return false;
- }
-
- // Initialize the EGL device connection
- if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE)
- {
- // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
- TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
- return false;
- }
-
- // Get an appropriate EGL framebuffer configuration
- eglChooseConfig(platform.device, framebufferAttribs, &platform.config, 1, &numConfigs);
-
- // Set rendering API
- eglBindAPI(EGL_OPENGL_ES_API);
-
- // Create an EGL rendering context
- platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs);
- if (platform.context == EGL_NO_CONTEXT)
- {
- TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
- return -1;
- }
-
- // Create an EGL window surface
- EGLint displayFormat = 0;
-
- // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry()
- // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID
- eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
-
- // Android specific call
- ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat); // Force use of native display size
-
- platform.surface = eglCreateWindowSurface(platform.device, platform.config, platform.app->window, NULL);
-
- // There must be at least one frame displayed before the buffers are swapped
- eglSwapInterval(platform.device, 1);
-
- EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context);
-
- // Check surface and context activation
- if (result != EGL_FALSE)
+ // TODO: Check display, device and context activation
+ bool result = true;
+ if (result)
{
CORE.Window.ready = true;
diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c
index c8fe0cfe7..b52ca1bf0 100644
--- a/src/platforms/rcore_web.c
+++ b/src/platforms/rcore_web.c
@@ -76,6 +76,13 @@ 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
} PlatformData;
//----------------------------------------------------------------------------------
@@ -129,7 +136,7 @@ static void MouseEnterCallback(GLFWwindow *window, int enter);
// Emscripten window callback events
static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData);
-// static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
+//static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
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);
@@ -142,7 +149,11 @@ static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPoin
static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData);
static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData);
-static const char *GetCanvasId(void);
+// 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
@@ -181,8 +192,8 @@ void ToggleFullscreen(void)
const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
if (wasFullscreen)
{
- if (CORE.Window.flags & FLAG_FULLSCREEN_MODE) enterFullscreen = false;
- else if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) enterFullscreen = true;
+ 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);
@@ -193,24 +204,21 @@ void ToggleFullscreen(void)
EM_ASM(document.exitFullscreen(););
- CORE.Window.fullscreen = false;
- CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
- CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
+ 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()
- {
+ EM_ASM(
+ setTimeout(function(){
Module.requestFullscreen(false, false);
}, 100);
);
- CORE.Window.fullscreen = true;
- CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
+
+ FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
}
// NOTE: Old notes below:
@@ -227,17 +235,17 @@ 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:
// 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);
+ //emscripten_request_fullscreen(platform.canvasId, 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
+ // 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,
@@ -245,7 +253,7 @@ void ToggleFullscreen(void)
// .canvasResizedCallback = EmscriptenWindowResizedCallback,
// .canvasResizedCallbackUserData = NULL
// };
- //emscripten_request_fullscreen_strategy("#canvas", EM_FALSE, &strategy);
+ //emscripten_request_fullscreen_strategy(platform.canvasId, EM_FALSE, &strategy);
// Option 3: Request fullscreen for the canvas element with strategy
// It works as expected but only inside the browser (client area)
@@ -256,14 +264,14 @@ void ToggleFullscreen(void)
.canvasResizedCallback = EmscriptenWindowResizedCallback,
.canvasResizedCallbackUserData = NULL
};
- emscripten_enter_soft_fullscreen("#canvas", &strategy);
+ emscripten_enter_soft_fullscreen(platform.canvasId, &strategy);
- int width, height;
- emscripten_get_canvas_element_size("#canvas", &width, &height);
+ int width = 0;
+ int height = 0;
+ 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
- CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
+ FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
}
else
{
@@ -271,11 +279,10 @@ void ToggleFullscreen(void)
//emscripten_exit_soft_fullscreen();
int width, height;
- emscripten_get_canvas_element_size("#canvas", &width, &height);
+ 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
- CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
}
*/
}
@@ -289,8 +296,8 @@ void ToggleBorderlessWindowed(void)
const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
if (wasFullscreen)
{
- if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) enterBorderless = false;
- else if (CORE.Window.flags & FLAG_FULLSCREEN_MODE) enterBorderless = true;
+ 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);
@@ -301,16 +308,15 @@ void ToggleBorderlessWindowed(void)
EM_ASM(document.exitFullscreen(););
- CORE.Window.fullscreen = false;
- CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
- CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+ FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
}
else enterBorderless = true;
if (enterBorderless)
{
- // NOTE: 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
+ // 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()
@@ -322,14 +328,14 @@ void ToggleBorderlessWindowed(void)
}, 100);
}, 100);
);
- CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE;
+ FLAG_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
}
}
// Set window state: maximized, if resizable
void MaximizeWindow(void)
{
- if ((glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) && !(CORE.Window.flags & FLAG_WINDOW_MAXIMIZED))
+ if ((glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) && !(FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)))
{
platform.unmaximizedWidth = CORE.Window.screen.width;
platform.unmaximizedHeight = CORE.Window.screen.height;
@@ -339,7 +345,7 @@ void MaximizeWindow(void)
if (tabWidth && tabHeight) glfwSetWindowSize(platform.handle, tabWidth, tabHeight);
- CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
}
}
@@ -352,11 +358,11 @@ void MinimizeWindow(void)
// Restore window from being minimized/maximized
void RestoreWindow(void)
{
- if ((glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) && (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED))
+ if ((glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) && (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)))
{
if (platform.unmaximizedWidth && platform.unmaximizedHeight) glfwSetWindowSize(platform.handle, platform.unmaximizedWidth, platform.unmaximizedHeight);
- CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
}
}
@@ -369,13 +375,13 @@ void SetWindowState(unsigned int flags)
// NOTE: In most cases the functions already change the flags internally
// State change: FLAG_VSYNC_HINT
- if ((flags & FLAG_VSYNC_HINT) > 0)
+ 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 ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)
+ 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);
@@ -383,13 +389,13 @@ void SetWindowState(unsigned int flags)
{
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 ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) || canvasStyleWidth > canvasWidth) ToggleBorderlessWindowed();
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) || canvasStyleWidth > canvasWidth) ToggleBorderlessWindowed();
}
else ToggleBorderlessWindowed();
}
// State change: FLAG_FULLSCREEN_MODE
- if ((flags & FLAG_FULLSCREEN_MODE) > 0)
+ 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);
@@ -397,38 +403,38 @@ void SetWindowState(unsigned int flags)
{
const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
const int screenWidth = EM_ASM_INT( { return screen.width; }, 0);
- if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) || screenWidth == canvasWidth ) ToggleFullscreen();
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) || screenWidth == canvasWidth ) ToggleFullscreen();
}
else ToggleFullscreen();
}
// State change: FLAG_WINDOW_RESIZABLE
- if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)) && (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)))
{
glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_TRUE);
- CORE.Window.flags |= FLAG_WINDOW_RESIZABLE;
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE);
}
// State change: FLAG_WINDOW_UNDECORATED
- if ((flags & FLAG_WINDOW_UNDECORATED) > 0)
+ 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 ((flags & FLAG_WINDOW_HIDDEN) > 0)
+ 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 ((flags & FLAG_WINDOW_MINIMIZED) > 0)
+ 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 (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
+ 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 (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
{
@@ -440,24 +446,24 @@ void SetWindowState(unsigned int flags)
if (tabWidth && tabHeight) glfwSetWindowSize(platform.handle, tabWidth, tabHeight);
- CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
+ FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
}
}
// State change: FLAG_WINDOW_UNFOCUSED
- if ((flags & FLAG_WINDOW_UNFOCUSED) > 0)
+ 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 ((flags & FLAG_WINDOW_TOPMOST) > 0)
+ 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 ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN))
{
TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_ALWAYS_RUN) not available on target platform");
}
@@ -466,31 +472,31 @@ void SetWindowState(unsigned int flags)
// NOTE: Review for PLATFORM_WEB
// State change: FLAG_WINDOW_TRANSPARENT
- if ((flags & FLAG_WINDOW_TRANSPARENT) > 0)
+ 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 ((flags & FLAG_WINDOW_HIGHDPI) > 0)
+ 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 ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)
+ 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 ((flags & FLAG_MSAA_4X_HINT) > 0)
+ 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 ((flags & FLAG_INTERLACED_HINT) > 0)
+ if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
{
TRACELOG(LOG_WARNING, "SetWindowState(FLAG_INTERLACED_HINT) not available on target platform");
}
@@ -503,90 +509,89 @@ void ClearWindowState(unsigned int flags)
// NOTE: In most cases the functions already change the flags internally
// State change: FLAG_VSYNC_HINT
- if ((flags & FLAG_VSYNC_HINT) > 0)
+ 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 ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)
+ 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 ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) || (screenWidth == canvasWidth)) EM_ASM(document.exitFullscreen(););
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE) || (screenWidth == canvasWidth)) EM_ASM(document.exitFullscreen(););
}
- CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
}
// State change: FLAG_FULLSCREEN_MODE
- if ((flags & FLAG_FULLSCREEN_MODE) > 0)
+ 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 ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) || (canvasStyleWidth > canvasWidth)) EM_ASM(document.exitFullscreen(););
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE) || (canvasStyleWidth > canvasWidth)) EM_ASM(document.exitFullscreen(););
}
- CORE.Window.fullscreen = false;
- CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
}
// State change: FLAG_WINDOW_RESIZABLE
- if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) && (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)))
{
glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_FALSE);
- CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_RESIZABLE);
}
// State change: FLAG_WINDOW_HIDDEN
- if ((flags & FLAG_WINDOW_HIDDEN) > 0)
+ 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 ((flags & FLAG_WINDOW_MINIMIZED) > 0)
+ 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 (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
+ if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) && (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED)))
{
if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
{
if (platform.unmaximizedWidth && platform.unmaximizedHeight) glfwSetWindowSize(platform.handle, platform.unmaximizedWidth, platform.unmaximizedHeight);
- CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
}
}
// State change: FLAG_WINDOW_UNDECORATED
- if ((flags & FLAG_WINDOW_UNDECORATED) > 0)
+ 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 ((flags & FLAG_WINDOW_UNFOCUSED) > 0)
+ 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 ((flags & FLAG_WINDOW_TOPMOST) > 0)
+ 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 ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)
+ if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN))
{
TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_ALWAYS_RUN) not available on target platform");
}
@@ -595,31 +600,31 @@ void ClearWindowState(unsigned int flags)
// NOTE: Review for PLATFORM_WEB
// State change: FLAG_WINDOW_TRANSPARENT
- if ((flags & FLAG_WINDOW_TRANSPARENT) > 0)
+ 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 ((flags & FLAG_WINDOW_HIGHDPI) > 0)
+ 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 ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)
+ 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 ((flags & FLAG_MSAA_4X_HINT) > 0)
+ 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 ((flags & FLAG_INTERLACED_HINT) > 0)
+ if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
{
TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_INTERLACED_HINT) not available on target platform");
}
@@ -663,7 +668,7 @@ void SetWindowMinSize(int width, int height)
CORE.Window.screenMin.height = height;
// Trigger the resize event once to update the window minimum width and height
- if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
}
// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
@@ -673,7 +678,7 @@ void SetWindowMaxSize(int width, int height)
CORE.Window.screenMax.height = height;
// Trigger the resize event once to update the window maximum width and height
- if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
}
// Set window dimensions
@@ -866,8 +871,7 @@ void EnableCursor(void)
// Disables cursor (lock cursor)
void DisableCursor(void)
{
- // TODO: figure out how not to hard code the canvas ID here.
- emscripten_request_pointerlock(GetCanvasId(), 1);
+ emscripten_request_pointerlock(platform.canvasId, 1);
// Set cursor position in the middle
SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
@@ -878,7 +882,32 @@ void DisableCursor(void)
// 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 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);
+#else
glfwSwapBuffers(platform.handle);
+#endif
}
//----------------------------------------------------------------------------------
@@ -893,10 +922,9 @@ double GetTime(void)
}
// 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.
-// Ref: https://github.com/raysan5/raylib/issues/686
+// 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
@@ -970,7 +998,7 @@ void SetMouseCursor(int cursor)
}
}
-// Get physical key name.
+// Get physical key name
const char *GetKeyName(int key)
{
TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
@@ -1090,10 +1118,6 @@ void PollInputEvents(void)
}
CORE.Window.resizedLastFrame = false;
-
- // TODO: This code does not seem to do anything??
- //if (CORE.Window.eventWaiting) glfwWaitEvents(); // Wait for in input events before continue (drawing is paused)
- //else glfwPollEvents(); // Poll input events: keyboard/mouse/window events (callbacks) --> WARNING: Where is key input reset?
}
//----------------------------------------------------------------------------------
@@ -1103,6 +1127,8 @@ void PollInputEvents(void)
// Initialize platform: graphics, inputs and more
int InitPlatform(void)
{
+ SetCanvasIdJs(platform.canvasId, 64); // Get the current canvas id
+
glfwSetErrorCallback(ErrorCallback);
// Initialize GLFW internal global state
@@ -1122,27 +1148,25 @@ int InitPlatform(void)
// glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers
// Check window creation flags
- if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true;
-
- if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window
+ 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
- if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED)) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window
- if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable
// Disable FLAG_WINDOW_MINIMIZED, not supported on initialization
- if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
// Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization
- if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
- if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
- if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TOPMOST)) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
// NOTE: Some GLFW flags are not supported on HTML5
@@ -1150,10 +1174,10 @@ int InitPlatform(void)
// Scale content area based on the monitor content scale where window is placed on
// NOTE: This feature requires emscripten 3.1.51
- //if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
+ //if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
//else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
- if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
{
// NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs
TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
@@ -1161,8 +1185,8 @@ int InitPlatform(void)
}
// NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version
- // with backward compatibility to older OpenGL versions.
- // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context.
+ // with backward compatibility to older OpenGL versions
+ // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context
// Check selection OpenGL version
if (rlGetVersion() == RL_OPENGL_21)
@@ -1172,10 +1196,12 @@ int InitPlatform(void)
}
else if (rlGetVersion() == RL_OPENGL_33)
{
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint)
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
- glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above!
- // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint)
+ 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_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above!
// glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context
}
@@ -1198,7 +1224,6 @@ int InitPlatform(void)
}
else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context
{
- // TODO: It seems WebGL 2.0 context is not set despite being requested
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
@@ -1211,14 +1236,28 @@ int InitPlatform(void)
// Init fullscreen toggle required var:
platform.ourFullscreen = false;
-
- if (CORE.Window.fullscreen)
+
+#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);
+ EM_ASM({
+ 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 (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))
{
- // 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.
+ // 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;
}
@@ -1251,18 +1290,6 @@ int InitPlatform(void)
TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
- // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example,
- // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3),
- // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched
- // by the sides to fit all monitor space...
-
- // Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight
- // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset)
- // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale
- // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed...
- // HighDPI monitors are properly considered in a following similar function: SetupViewport()
- SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
-
platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL);
// NOTE: Full-screen change, not working properly...
@@ -1286,6 +1313,7 @@ int InitPlatform(void)
TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
return -1;
}
+#endif
// WARNING: glfwCreateWindow() title doesn't work with emscripten
emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " ");
@@ -1296,7 +1324,7 @@ int InitPlatform(void)
glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback);
glfwSetDropCallback(platform.handle, WindowDropCallback);
- if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
{
// Window content (framebuffer) scale callback
glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback);
@@ -1338,7 +1366,7 @@ int InitPlatform(void)
return -1;
}
- if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
+ 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; }
@@ -1352,14 +1380,14 @@ int InitPlatform(void)
//----------------------------------------------------------------------------
// Setup window events callbacks
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenFullscreenChangeCallback);
- emscripten_set_blur_callback(GetCanvasId(), platform.handle, 1, EmscriptenFocusCallback);
- emscripten_set_focus_callback(GetCanvasId(), platform.handle, 1, EmscriptenFocusCallback);
+ emscripten_set_blur_callback(platform.canvasId, platform.handle, 1, EmscriptenFocusCallback);
+ emscripten_set_focus_callback(platform.canvasId, platform.handle, 1, EmscriptenFocusCallback);
emscripten_set_visibilitychange_callback(NULL, 1, EmscriptenVisibilityChangeCallback);
// WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review
- // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas)
+ // Check fullscreen change events(note this is done on the window since most browsers don't support this on canvas)
// emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
- // Check Resize event (note this is done on the window since most browsers don't support this on #canvas)
+ // Check Resize event (note this is done on the window since most browsers don't support this on canvas)
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
// Trigger resize callback to force initial size
@@ -1367,15 +1395,15 @@ int InitPlatform(void)
// Setup input events
// NOTE: Keyboard callbacks only used to consume some events, libglfw.js takes care of the actual input
- //emscripten_set_keypress_callback(GetCanvasId(), NULL, 1, EmscriptenKeyboardCallback); // WRNING: Breaks input
- //emscripten_set_keydown_callback(GetCanvasId(), NULL, 1, EmscriptenKeyboardCallback);
- emscripten_set_click_callback(GetCanvasId(), NULL, 1, EmscriptenMouseCallback);
+ //emscripten_set_keypress_callback(platform.canvasId, NULL, 1, EmscriptenKeyboardCallback); // WRNING: Breaks input
+ //emscripten_set_keydown_callback(platform.canvasId, NULL, 1, EmscriptenKeyboardCallback);
+ emscripten_set_click_callback(platform.canvasId, NULL, 1, EmscriptenMouseCallback);
emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenPointerlockCallback);
- emscripten_set_mousemove_callback(GetCanvasId(), NULL, 1, EmscriptenMouseMoveCallback);
- emscripten_set_touchstart_callback(GetCanvasId(), NULL, 1, EmscriptenTouchCallback);
- emscripten_set_touchend_callback(GetCanvasId(), NULL, 1, EmscriptenTouchCallback);
- emscripten_set_touchmove_callback(GetCanvasId(), NULL, 1, EmscriptenTouchCallback);
- emscripten_set_touchcancel_callback(GetCanvasId(), NULL, 1, EmscriptenTouchCallback);
+ emscripten_set_mousemove_callback(platform.canvasId, NULL, 1, EmscriptenMouseMoveCallback);
+ 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);
//----------------------------------------------------------------------------
@@ -1423,7 +1451,7 @@ static void WindowSizeCallback(GLFWwindow *window, int width, int height)
if (IsWindowFullscreen()) return;
// Set current screen size
- if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
{
Vector2 windowScaleDPI = GetWindowScaleDPI();
@@ -1448,15 +1476,15 @@ static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float s
// GLFW3: Called on windows minimized/restored
static void WindowIconifyCallback(GLFWwindow *window, int iconified)
{
- if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified
- else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored
+ if (iconified) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was iconified
+ else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was restored
}
// GLFW3: Called on windows get/lose focus
static void WindowFocusCallback(GLFWwindow *window, int focused)
{
- if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // The window was focused
- else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; // The window lost focus
+ if (focused) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window was focused
+ else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window lost focus
}
// GLFW3: Called on file-drop over the window
@@ -1482,7 +1510,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);
}
}
}
@@ -1517,8 +1545,8 @@ static void CharCallback(GLFWwindow *window, unsigned int key)
// NOTE: Registers any key down considering OS keyboard layout but
// does not detect action events, those should be managed by user...
- // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
- // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char
+ // REF: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
+ // REF: https://www.glfw.org/docs/latest/input_guide.html#input_char
// Check if there is space available in the queue
if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
@@ -1671,8 +1699,8 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE
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++) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]);
+ for (int i = 0; i < gamepadEvent->numButtons; i++) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]);
*/
if (gamepadEvent->connected && (gamepadEvent->index < MAX_GAMEPADS))
@@ -1696,7 +1724,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent
// 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(GetCanvasId(), &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++)
{
@@ -1714,7 +1742,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent
else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0;
}
- // Update mouse position if we detect a single touch.
+ // 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;
@@ -1781,9 +1809,8 @@ 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;
- CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
- CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
+ FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+ FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
}
}
@@ -1794,7 +1821,7 @@ static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const Emscripte
static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
{
// Don't resize non-resizeable windows
- if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) return 1;
+ if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) return 1;
// This event is called whenever the window changes sizes,
// so the size of the canvas object is explicitly retrieved below
@@ -1807,7 +1834,7 @@ static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *
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(GetCanvasId(), width, height);
+ emscripten_set_canvas_element_size(platform.canvasId, width, height);
SetupViewport(width, height); // Reset viewport and projection matrix for new size
@@ -1844,27 +1871,10 @@ static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent
// Emscripten: Called on visibility change events
static EM_BOOL EmscriptenVisibilityChangeCallback(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData)
{
- if (visibilityChangeEvent->hidden) CORE.Window.flags |= FLAG_WINDOW_HIDDEN; // The window was hidden
- else CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // The window was restored
+ 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
}
//-------------------------------------------------------------------------------------------------------
-// JS: Get the canvas id provided by the module configuration
-EM_JS(char *, GetCanvasIdJs, (), {
- var canvasId = "#" + Module.canvas.id;
- var lengthBytes = lengthBytesUTF8(canvasId) + 1;
- var stringOnWasmHeap = _malloc(lengthBytes);
- stringToUTF8(canvasId, stringOnWasmHeap, lengthBytes);
- return stringOnWasmHeap;
-});
-
-// Get canvas id (using embedded JS function)
-static const char *GetCanvasId(void)
-{
- static char *canvasId = NULL;
- if (canvasId == NULL) canvasId = GetCanvasIdJs();
- return canvasId;
-}
-
// EOF
diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c
new file mode 100644
index 000000000..aeead6d9b
--- /dev/null
+++ b/src/platforms/rcore_web_emscripten.c
@@ -0,0 +1,1693 @@
+/**********************************************************************************************
+*
+* rcore_web_emscripten - Functions to manage window, graphics device and inputs
+*
+* PLATFORM: WEB - EMSCRIPTEN
+* - HTML5 (WebAssembly)
+*
+* LIMITATIONS:
+* - TBD
+*
+* POSSIBLE IMPROVEMENTS:
+* - TBD
+*
+* ADDITIONAL NOTES:
+* - TRACELOG() function is located in raylib [utils] module
+*
+* CONFIGURATION:
+* #define RCORE_PLATFORM_CUSTOM_FLAG
+* Custom flag for rcore on target platform -not used-
+*
+* DEPENDENCIES:
+* - emscripten: Allow interaction between browser API and C
+* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
+*
+*
+* LICENSE: zlib/libpng
+*
+* Copyright (c) 2025 Ramon Santamaria (@raysan5) and contributors
+*
+* This software is provided "as-is", without any express or implied warranty. In no event
+* will the authors be held liable for any damages arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose, including commercial
+* applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not claim that you
+* wrote the original software. If you use this software in a product, an acknowledgment
+* in the product documentation would be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
+* as being the original software.
+*
+* 3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#include // Emscripten functionality for C
+#include // Emscripten HTML5 library
+
+#include // Required for: timespec, nanosleep(), select() - POSIX
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#if (_POSIX_C_SOURCE < 199309L)
+ #undef _POSIX_C_SOURCE
+ #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef struct {
+ char canvasId[64]; // Current canvas id
+ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE glContext; // OpenGL context
+ unsigned int *pixels; // Pointer to pixel data buffer (RGBA 32bit format)
+} PlatformData;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+extern CoreData CORE; // Global CORE state context
+
+static PlatformData platform = { 0 }; // Platform specific data
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static const char cursorLUT[11][12] = {
+ "default", // 0 MOUSE_CURSOR_DEFAULT
+ "default", // 1 MOUSE_CURSOR_ARROW
+ "text", // 2 MOUSE_CURSOR_IBEAM
+ "crosshair", // 3 MOUSE_CURSOR_CROSSHAIR
+ "pointer", // 4 MOUSE_CURSOR_POINTING_HAND
+ "ew-resize", // 5 MOUSE_CURSOR_RESIZE_EW
+ "ns-resize", // 6 MOUSE_CURSOR_RESIZE_NS
+ "nwse-resize", // 7 MOUSE_CURSOR_RESIZE_NWSE
+ "nesw-resize", // 8 MOUSE_CURSOR_RESIZE_NESW
+ "move", // 9 MOUSE_CURSOR_RESIZE_ALL
+ "not-allowed" // 10 MOUSE_CURSOR_NOT_ALLOWED
+};
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+int InitPlatform(void); // Initialize platform (graphics, inputs and more)
+void ClosePlatform(void); // Close platform
+
+// Emscripten window callback events
+static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
+static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData);
+static EM_BOOL EmscriptenVisibilityChangeCallback(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData);
+static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData);
+// TODO: Implement GLFW3 alternative for drop callback, runs when drop files into browser/canvas
+//static void WindowDropCallback(GLFWwindow *window, int count, const char **paths);
+
+// Emscripten input callback events
+static EM_BOOL EmscriptenKeyboardCallback(int eventType, const EmscriptenKeyboardEvent *keyboardEvent, void *userData);
+static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
+static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
+static EM_BOOL EmscriptenMouseWheelCallback(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData);
+static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData);
+static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData);
+static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData);
+
+// JS: Set the canvas id provided by the module configuration
+EM_JS(void, SetCanvasIdJs, (char *out, int outSize), {
+ var canvasId = "#" + Module.canvas.id;
+ stringToUTF8(canvasId, out, outSize);
+});
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+// NOTE: Functions declaration is provided by raylib.h
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Window and Graphics Device
+//----------------------------------------------------------------------------------
+
+// Check if application should close
+// This will always return false on a web-build as web builds have no control over this functionality
+// Sleep is handled in EndDrawing() for synchronous code
+bool WindowShouldClose(void)
+{
+ // Emscripten Asyncify is required to run synchronous code in asynchronous JS
+ // REF: https://emscripten.org/docs/porting/asyncify.html
+
+ // WindowShouldClose() is not called on a web-ready raylib application if using emscripten_set_main_loop()
+ // and encapsulating one frame execution on a UpdateDrawFrame() function,
+ // allowing the browser to manage execution asynchronously
+
+ // Optionally we can manage the time we give-control-back-to-browser if required,
+ // but it seems below line could generate stuttering on some browsers
+ emscripten_sleep(12);
+
+ return false;
+}
+
+// Toggle fullscreen mode
+void ToggleFullscreen(void)
+{
+ bool enterFullscreen = false;
+
+ const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
+ if (wasFullscreen)
+ {
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) enterFullscreen = false;
+ else if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) enterFullscreen = true;
+ else
+ {
+ const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
+ const int canvasStyleWidth = EM_ASM_INT( { return parseInt(Module.canvas.style.width); }, 0);
+ if (canvasStyleWidth > canvasWidth) enterFullscreen = false;
+ else enterFullscreen = true;
+ }
+
+ EM_ASM(document.exitFullscreen(););
+
+ 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;
+ }
+
+ //TRACELOGD("INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]);
+ }
+
+ // Register axis data for every connected gamepad
+ for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXES); j++)
+ {
+ CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j];
+ }
+
+ CORE.Input.Gamepad.axisCount[i] = gamepadState.numAxes;
+ }
+ }
+
+ CORE.Window.resizedLastFrame = false;
+}
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+
+// Initialize platform: graphics, inputs and more
+int InitPlatform(void)
+{
+ SetCanvasIdJs(platform.canvasId, 64); // Get the current canvas id
+
+ // Initialize graphic device: display/window and graphic context
+ //----------------------------------------------------------------------------
+ emscripten_set_canvas_element_size(platform.canvasId, CORE.Window.screen.width, CORE.Window.screen.height);
+ EmscriptenWebGLContextAttributes attribs = { 0 };
+ emscripten_webgl_init_context_attributes(&attribs);
+ attribs.alpha = EM_TRUE;
+ attribs.depth = EM_TRUE;
+ attribs.stencil = EM_FALSE;
+ attribs.antialias = EM_FALSE;
+
+ // Check window creation flags
+ // 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)
+{
+ /*
+ TRACELOGD("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"",
+ eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state",
+ gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping);
+
+ for (int i = 0; i < gamepadEvent->numAxes; i++) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]);
+ for (int i = 0; i < gamepadEvent->numButtons; i++) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]);
+ */
+
+ if (gamepadEvent->connected && (gamepadEvent->index < MAX_GAMEPADS))
+ {
+ CORE.Input.Gamepad.ready[gamepadEvent->index] = true;
+ snprintf(CORE.Input.Gamepad.name[gamepadEvent->index], MAX_GAMEPAD_NAME_LENGTH, "%s", gamepadEvent->id);
+ }
+ else CORE.Input.Gamepad.ready[gamepadEvent->index] = false;
+
+ return 1; // The event was consumed by the callback handler
+}
+
+// Emscripten: Called on touch input events
+static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
+{
+ // Register touch points count
+ CORE.Input.Touch.pointCount = touchEvent->numTouches;
+
+ double canvasWidth = 0.0;
+ double canvasHeight = 0.0;
+ // NOTE: emscripten_get_canvas_element_size() returns canvas.width and canvas.height but
+ // we are looking for actual CSS size: canvas.style.width and canvas.style.height
+ // EMSCRIPTEN_RESULT res = emscripten_get_canvas_element_size("#canvas", &canvasWidth, &canvasHeight);
+ emscripten_get_element_css_size(platform.canvasId, &canvasWidth, &canvasHeight);
+
+ for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
+ {
+ // Register touch points id
+ CORE.Input.Touch.pointId[i] = touchEvent->touches[i].identifier;
+
+ // Register touch points position
+ CORE.Input.Touch.position[i] = (Vector2){touchEvent->touches[i].targetX, touchEvent->touches[i].targetY};
+
+ // Normalize gestureEvent.position[x] for CORE.Window.screen.width and CORE.Window.screen.height
+ CORE.Input.Touch.position[i].x *= ((float)GetScreenWidth()/(float)canvasWidth);
+ CORE.Input.Touch.position[i].y *= ((float)GetScreenHeight()/(float)canvasHeight);
+
+ if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) CORE.Input.Touch.currentTouchState[i] = 1;
+ else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0;
+ }
+
+ // Update mouse position if we detect a single touch
+ if (CORE.Input.Touch.pointCount == 1)
+ {
+ CORE.Input.Mouse.currentPosition.x = CORE.Input.Touch.position[0].x;
+ CORE.Input.Mouse.currentPosition.y = CORE.Input.Touch.position[0].y;
+ }
+
+#if defined(SUPPORT_GESTURES_SYSTEM)
+ GestureEvent gestureEvent = { 0 };
+ gestureEvent.pointCount = CORE.Input.Touch.pointCount;
+
+ // Register touch actions
+ if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
+ else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) gestureEvent.touchAction = TOUCH_ACTION_UP;
+ else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE;
+ else if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL;
+
+ for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++)
+ {
+ gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i];
+ gestureEvent.position[i] = CORE.Input.Touch.position[i];
+
+ // Normalize gestureEvent.position[i]
+ gestureEvent.position[i].x /= (float)GetScreenWidth();
+ gestureEvent.position[i].y /= (float)GetScreenHeight();
+ }
+
+ // Gesture data is sent to gestures system for processing
+ ProcessGestureEvent(gestureEvent);
+#endif
+
+ if (eventType == EMSCRIPTEN_EVENT_TOUCHEND)
+ {
+ // Identify the EMSCRIPTEN_EVENT_TOUCHEND and remove it from the list
+ for (int i = 0; i < CORE.Input.Touch.pointCount; i++)
+ {
+ if (touchEvent->touches[i].isChanged)
+ {
+ // Move all touch points one position up
+ for (int j = i; j < CORE.Input.Touch.pointCount - 1; j++)
+ {
+ CORE.Input.Touch.pointId[j] = CORE.Input.Touch.pointId[j + 1];
+ CORE.Input.Touch.position[j] = CORE.Input.Touch.position[j + 1];
+ }
+ // Decrease touch points count to remove the last one
+ CORE.Input.Touch.pointCount--;
+ break;
+ }
+ }
+ // Clamp pointCount to avoid negative values
+ if (CORE.Input.Touch.pointCount < 0) CORE.Input.Touch.pointCount = 0;
+ }
+
+ return 1; // The event was consumed by the callback handler
+}
+//-------------------------------------------------------------------------------------------------------
+
+// EOF
diff --git a/src/raudio.c b/src/raudio.c
index de2bf81b2..c65aaa134 100644
--- a/src/raudio.c
+++ b/src/raudio.c
@@ -593,7 +593,7 @@ AudioBuffer *LoadAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam
// Init audio buffer values
audioBuffer->volume = 1.0f;
audioBuffer->pitch = 1.0f;
- audioBuffer->pan = 0.5f;
+ audioBuffer->pan = 0.0f; // Center
audioBuffer->callback = NULL;
audioBuffer->processor = NULL;
@@ -720,7 +720,7 @@ void SetAudioBufferPitch(AudioBuffer *buffer, float pitch)
// Set pan for an audio buffer
void SetAudioBufferPan(AudioBuffer *buffer, float pan)
{
- if (pan < 0.0f) pan = 0.0f;
+ if (pan < -1.0f) pan = -1.0f;
else if (pan > 1.0f) pan = 1.0f;
if (buffer != NULL)
@@ -985,10 +985,10 @@ Sound LoadSoundAlias(Sound source)
audioBuffer->sizeInFrames = source.stream.buffer->sizeInFrames;
audioBuffer->data = source.stream.buffer->data;
- // initalize the buffer as if it was new
+ // Initalize the buffer as if it was new
audioBuffer->volume = 1.0f;
audioBuffer->pitch = 1.0f;
- audioBuffer->pan = 0.5f;
+ audioBuffer->pan = 0.0f; // Center
sound.frameCount = source.frameCount;
sound.stream.sampleRate = AUDIO.System.device.sampleRate;
@@ -1040,8 +1040,8 @@ void UnloadSoundAlias(Sound alias)
}
// Update sound buffer with new data
-// NOTE 1: data format must match sound.stream.sampleSize
-// NOTE 2: frameCount must not exceed sound.frameCount
+// PARAMS: [data], format must match sound.stream.sampleSize, default 32 bit float - stereo
+// PARAMS: [frameCount] must not exceed sound.frameCount
void UpdateSound(Sound sound, const void *data, int frameCount)
{
if (sound.stream.buffer != NULL)
@@ -1140,7 +1140,7 @@ bool ExportWaveAsCode(Wave wave, const char *fileName)
// Get file name from path and convert variable name to uppercase
char varFileName[256] = { 0 };
- strcpy(varFileName, GetFileNameWithoutExt(fileName));
+ strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1);
for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; }
// Add wave information
@@ -2092,9 +2092,7 @@ float GetMusicTimePlayed(Music music)
int framesInFirstBuffer = music.stream.buffer->isSubBufferProcessed[0]? 0 : subBufferSize;
int framesInSecondBuffer = music.stream.buffer->isSubBufferProcessed[1]? 0 : subBufferSize;
int framesInBuffers = framesInFirstBuffer + framesInSecondBuffer;
- if ((unsigned int)framesInBuffers > music.frameCount) {
- if (!music.looping) framesInBuffers = music.frameCount;
- }
+ if (((unsigned int)framesInBuffers > music.frameCount) && !music.looping) framesInBuffers = music.frameCount;
int framesSentToMix = music.stream.buffer->frameCursorPos%subBufferSize;
int framesPlayed = (framesProcessed - framesInBuffers + framesSentToMix)%(int)music.frameCount;
if (framesPlayed < 0) framesPlayed += music.frameCount;
@@ -2125,7 +2123,7 @@ AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, un
if (deviceBitsPerSample > 4) deviceBitsPerSample = 4;
deviceBitsPerSample *= AUDIO.System.device.playback.channels;
- unsigned int subBufferSize = (AUDIO.Buffer.defaultSize == 0) ? (AUDIO.System.device.sampleRate/30*deviceBitsPerSample) : AUDIO.Buffer.defaultSize;
+ unsigned int subBufferSize = (AUDIO.Buffer.defaultSize == 0)? (AUDIO.System.device.sampleRate/30*deviceBitsPerSample) : AUDIO.Buffer.defaultSize;
if (subBufferSize < periodSize) subBufferSize = periodSize;
@@ -2605,8 +2603,8 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 fr
if (channels == 2) // We consider panning
{
- const float left = buffer->pan;
- const float right = 1.0f - left;
+ const float right = (buffer->pan + 1.0f)/2.0f; // Normalize: [-1..1] -> [0..1]
+ const float left = 1.0f - right;
// Fast sine approximation in [0..1] for pan law: y = 0.5f*x*(3 - x*x);
const float levels[2] = { localVolume*0.5f*left*(3.0f - left*left), localVolume*0.5f*right*(3.0f - right*right) };
@@ -2741,11 +2739,13 @@ static const char *GetFileExtension(const char *fileName)
return dot;
}
-// String pointer reverse break: returns right-most occurrence of charset in s
-static const char *strprbrk(const char *s, const char *charset)
+// String pointer reverse break: returns right-most occurrence of charset in text
+static const char *strprbrk(const char *text, const char *charset)
{
const char *latestMatch = NULL;
- for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { }
+
+ for (; (text != NULL) && (text = strpbrk(text, charset)); latestMatch = text++) { }
+
return latestMatch;
}
@@ -2768,11 +2768,11 @@ static const char *GetFileNameWithoutExt(const char *filePath)
static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH] = { 0 };
memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH);
- if (filePath != NULL) strcpy(fileName, GetFileName(filePath)); // Get filename with extension
+ if (filePath != NULL) strncpy(fileName, GetFileName(filePath), MAX_FILENAMEWITHOUTEXT_LENGTH - 1); // Get filename with extension
- int size = (int)strlen(fileName); // Get size in bytes
+ int fileNameLength = (int)strlen(fileName); // Get size in bytes
- for (int i = 0; (i < size) && (i < MAX_FILENAMEWITHOUTEXT_LENGTH); i++)
+ for (int i = 0; (i < fileNameLength) && (i < MAX_FILENAMEWITHOUTEXT_LENGTH); i++)
{
if (fileName[i] == '.')
{
@@ -2866,7 +2866,8 @@ static bool SaveFileText(const char *fileName, char *text)
if (file != NULL)
{
- int count = fprintf(file, "%s", text);
+ int count = 0;
+ if (text != NULL) 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);
diff --git a/src/raylib.h b/src/raylib.h
index e4be11084..ba80e40c7 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -33,7 +33,6 @@
* [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management
*
* OPTIONAL DEPENDENCIES (included):
-* [rcore] msf_gif (Miles Fogle) for GIF recording
* [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorithm
* [rcore] sdefl (Micha Mettke) for DEFLATE compression algorithm
* [rcore] rprand (Ramon Santamaria) for pseudo-random numbers generation
@@ -100,13 +99,13 @@
#define __declspec(x) __attribute__((x))
#endif
#if defined(BUILD_LIBTYPE_SHARED)
- #define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll)
+ #define RLAPI __declspec(dllexport) // 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)
+ #define RLAPI __declspec(dllimport) // Using the library as a Win32 shared library (.dll)
#endif
#else
#if defined(BUILD_LIBTYPE_SHARED)
- #define RLAPI __attribute__((visibility("default"))) // We are building as a Unix shared library (.so/.dylib)
+ #define RLAPI __attribute__((visibility("default"))) // Building as a Unix shared library (.so/.dylib)
#endif
#endif
@@ -158,7 +157,7 @@
#error "C++11 or later is required. Add -std=c++11"
#endif
-// NOTE: We set some defines with some data types declared by raylib
+// NOTE: Set some defines with some data types declared by raylib
// Other modules (raymath, rlgl) also require some of those types, so,
// to be able to use those other modules as standalone (not depending on raylib)
// this defines are very useful for internal check and avoid type (re)definitions
@@ -335,10 +334,10 @@ typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D
// Camera2D, defines position/orientation in 2d space
typedef struct Camera2D {
- Vector2 offset; // Camera offset (displacement from target)
- Vector2 target; // Camera target (rotation and zoom origin)
- float rotation; // Camera rotation in degrees
- float zoom; // Camera zoom (scaling), should be 1.0f by default
+ Vector2 offset; // Camera offset (screen space offset from window origin)
+ Vector2 target; // Camera target (world space target point that is mapped to screen space offset)
+ float rotation; // Camera rotation in degrees (pivots around target)
+ float zoom; // Camera zoom (scaling around target), must not be set to 0, set to 1.0f for no scale
} Camera2D;
// Mesh, vertex data and vao/vbo
@@ -1146,7 +1145,7 @@ RLAPI const char *GetPrevDirectoryPath(const char *dirPath); // Get previ
RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string)
RLAPI const char *GetApplicationDirectory(void); // Get the directory of the running application (uses static string)
RLAPI int MakeDirectory(const char *dirPath); // Create directories (including full path requested), returns 0 on success
-RLAPI bool ChangeDirectory(const char *dir); // Change working directory, return true on success
+RLAPI bool ChangeDirectory(const char *dirPath); // Change working directory, return true on success
RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory
RLAPI bool IsFileNameValid(const char *fileName); // Check if fileName is valid for the platform/OS
RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths
@@ -1658,7 +1657,7 @@ RLAPI Sound LoadSound(const char *fileName); // Load so
RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data
RLAPI Sound LoadSoundAlias(Sound source); // Create a new sound that shares the same sample data as the source sound, does not own the sound data
RLAPI bool IsSoundValid(Sound sound); // Checks if a sound is valid (data loaded and buffers initialized)
-RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data (data and frame count should fit in sound)
+RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data (default data format: 32 bit float, stereo)
RLAPI void UnloadWave(Wave wave); // Unload wave data
RLAPI void UnloadSound(Sound sound); // Unload sound
RLAPI void UnloadSoundAlias(Sound alias); // Unload a sound alias (does not deallocate sample data)
@@ -1673,7 +1672,7 @@ RLAPI void ResumeSound(Sound sound); // Resume
RLAPI bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing
RLAPI void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level)
RLAPI void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level)
-RLAPI void SetSoundPan(Sound sound, float pan); // Set pan for a sound (0.5 is center)
+RLAPI void SetSoundPan(Sound sound, float pan); // Set pan for a sound (-1.0 left, 0.0 center, 1.0 right)
RLAPI Wave WaveCopy(Wave wave); // Copy a wave to a new wave
RLAPI void WaveCrop(Wave *wave, int initFrame, int finalFrame); // Crop a wave to defined frames range
RLAPI void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // Convert wave data to desired format
@@ -1694,7 +1693,7 @@ RLAPI void ResumeMusicStream(Music music); // Resume
RLAPI void SeekMusicStream(Music music, float position); // Seek music to a position (in seconds)
RLAPI void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level)
RLAPI void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level)
-RLAPI void SetMusicPan(Music music, float pan); // Set pan for a music (0.5 is center)
+RLAPI void SetMusicPan(Music music, float pan); // Set pan for a music (-1.0 left, 0.0 center, 1.0 right)
RLAPI float GetMusicTimeLength(Music music); // Get music time length (in seconds)
RLAPI float GetMusicTimePlayed(Music music); // Get current music time played (in seconds)
diff --git a/src/raymath.h b/src/raymath.h
index 65a20de59..32dfd2b0a 100644
--- a/src/raymath.h
+++ b/src/raymath.h
@@ -2552,7 +2552,38 @@ RMAPI int QuaternionEquals(Quaternion p, Quaternion q)
return result;
}
+// Compose a transformation matrix from rotational, translational and scaling components
+// TODO: This function is not following raymath conventions defined in header: NOT self-contained
+RMAPI Matrix MatrixCompose(Vector3 translation, Quaternion rotation, Vector3 scale)
+{
+ // Initialize vectors
+ Vector3 right = { 1.0f, 0.0f, 0.0f };
+ Vector3 up = { 0.0f, 1.0f, 0.0f };
+ Vector3 forward = { 0.0f, 0.0f, 1.0f };
+
+ // Scale vectors
+ right = Vector3Scale(right, scale.x);
+ up = Vector3Scale(up, scale.y);
+ forward = Vector3Scale(forward , scale.z);
+
+ // Rotate vectors
+ right = Vector3RotateByQuaternion(right, rotation);
+ up = Vector3RotateByQuaternion(up, rotation);
+ forward = Vector3RotateByQuaternion(forward, rotation);
+
+ // Set result matrix output
+ Matrix result = {
+ right.x, up.x, forward.x, translation.x,
+ right.y, up.y, forward.y, translation.y,
+ right.z, up.z, forward.z, translation.z,
+ 0.0f, 0.0f, 0.0f, 1.0f
+ };
+
+ return result;
+}
+
// Decompose a transformation matrix into its rotational, translational and scaling components and remove shear
+// TODO: This function is not following raymath conventions defined in header: NOT self-contained
RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotation, Vector3 *scale)
{
float eps = (float)1e-9;
@@ -2587,10 +2618,7 @@ RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotatio
// X Scale
scl.x = Vector3Length(matColumns[0]);
- if (scl.x > eps)
- {
- matColumns[0] = Vector3Scale(matColumns[0], 1.0f / scl.x);
- }
+ if (scl.x > eps) matColumns[0] = Vector3Scale(matColumns[0], 1.0f / scl.x);
// Compute XY shear and make col2 orthogonal
shear[0] = Vector3DotProduct(matColumns[0], matColumns[1]);
diff --git a/src/rcore.c b/src/rcore.c
index d06e3089b..ea38300f1 100644
--- a/src/rcore.c
+++ b/src/rcore.c
@@ -28,9 +28,11 @@
* - Android (ARM, ARM64)
* > PLATFORM_DESKTOP_WIN32 (Native Win32):
* - Windows (Win32, Win64)
+* > PLATFORM_MEMORY
+* - Memory framebuffer output, using software renderer, no OS required
* CONFIGURATION:
* #define SUPPORT_DEFAULT_FONT (default)
-* Default font is loaded on window initialization to be available for the user to render simple text.
+* 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 (module: text)
*
* #define SUPPORT_CAMERA_SYSTEM
@@ -41,7 +43,7 @@
* Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag
*
* #define SUPPORT_MOUSE_GESTURES
-* Mouse gestures are directly mapped like touches and processed by gestures system.
+* Mouse gestures are directly mapped like touches and processed by gestures system
*
* #define SUPPORT_BUSY_WAIT_LOOP
* Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used
@@ -52,9 +54,6 @@
* #define SUPPORT_SCREEN_CAPTURE
* Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
*
-* #define SUPPORT_GIF_RECORDING
-* Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()
-*
* #define SUPPORT_COMPRESSION_API
* Support CompressData() and DecompressData() functions, those functions use zlib implementation
* provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module
@@ -95,12 +94,12 @@
//----------------------------------------------------------------------------------
#if (defined(__linux__) || defined(PLATFORM_WEB) || defined(PLATFORM_WEB_RGFW)) && (_XOPEN_SOURCE < 500)
#undef _XOPEN_SOURCE
- #define _XOPEN_SOURCE 500 // Required for: readlink if compiled with c99 without gnu ext.
+ #define _XOPEN_SOURCE 500 // Required for: readlink if compiled with c99 without GNU extensions
#endif
#if (defined(__linux__) || defined(PLATFORM_WEB) || defined(PLATFORM_WEB_RGFW)) && (_POSIX_C_SOURCE < 199309L)
#undef _POSIX_C_SOURCE
- #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
+ #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without GNU extensions
#endif
#include "raylib.h" // Declares module functions
@@ -114,10 +113,13 @@
#include // Required for: srand(), rand(), atexit()
#include // Required for: sprintf() [Used in OpenURL()]
-#include // Required for: strlen(), strcpy(), strcmp(), strrchr(), memset()
+#include // Required for: strlen(), strncpy(), strcmp(), strrchr(), memset()
#include // Required for: time() [Used in InitTimer()]
#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()]
+#if defined(PLATFORM_MEMORY) || defined(PLATFORM_WEB)
+ #define SW_GL_FRAMEBUFFER_COPY_BGRA false
+#endif
#define RLGL_IMPLEMENTATION
#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
@@ -134,15 +136,6 @@
#include "rcamera.h" // Camera system functionality
#endif
-#if defined(SUPPORT_GIF_RECORDING)
- #define MSF_GIF_MALLOC(contextPointer, newSize) RL_MALLOC(newSize)
- #define MSF_GIF_REALLOC(contextPointer, oldMemory, oldSize, newSize) RL_REALLOC(oldMemory, newSize)
- #define MSF_GIF_FREE(contextPointer, oldMemory, oldSize) RL_FREE(oldMemory)
-
- #define MSF_GIF_IMPL
- #include "external/msf_gif.h" // GIF recording functionality
-#endif
-
#if defined(SUPPORT_COMPRESSION_API)
#define SINFL_IMPLEMENTATION
#define SINFL_NO_SIMD
@@ -167,18 +160,18 @@
#define MAX_PATH 260
#endif
-struct HINSTANCE__;
-#if defined(__cplusplus)
-extern "C" {
-#endif
-__declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(struct HINSTANCE__ *hModule, char *lpFilename, unsigned long nSize);
-__declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(struct HINSTANCE__ *hModule, wchar_t *lpFilename, unsigned long nSize);
-__declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);
-__declspec(dllimport) unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod);
-__declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
-#if defined(__cplusplus)
-}
-#endif
+ struct HINSTANCE__;
+ #if defined(__cplusplus)
+ extern "C" {
+ #endif
+ __declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(struct HINSTANCE__ *hModule, char *lpFilename, unsigned long nSize);
+ __declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(struct HINSTANCE__ *hModule, wchar_t *lpFilename, unsigned long nSize);
+ __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);
+ __declspec(dllimport) unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod);
+ __declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
+ #if defined(__cplusplus)
+ }
+ #endif
#elif defined(__linux__)
#include
#elif defined(__FreeBSD__)
@@ -280,7 +273,7 @@ __declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod)
#define FLAG_SET(n, f) ((n) |= (f))
#define FLAG_CLEAR(n, f) ((n) &= ~(f))
#define FLAG_TOGGLE(n, f) ((n) ^= (f))
-#define FLAG_CHECK(n, f) ((n) & (f))
+#define FLAG_IS_SET(n, f) (((n) & (f)) == (f))
//----------------------------------------------------------------------------------
// Types and Structures Definition
@@ -294,20 +287,19 @@ typedef struct CoreData {
const char *title; // Window text title const pointer
unsigned int flags; // Configuration flags (bit based), keeps window state
bool ready; // Check if window has been initialized successfully
- bool fullscreen; // Check if fullscreen mode is enabled
bool shouldClose; // Check if window set for closing
bool resizedLastFrame; // Check if window has been resized last frame
bool eventWaiting; // Wait for events before ending frame
bool usingFbo; // Using FBO (RenderTexture) for rendering instead of default framebuffer
- Point position; // Window position (required on fullscreen toggle)
- Point previousPosition; // Window previous position (required on borderless windowed toggle)
Size display; // Display width and height (monitor, device-screen, LCD, ...)
- Size screen; // Screen width and height (used render area)
- Size previousScreen; // Screen previous width and height (required on borderless windowed toggle)
- Size currentFbo; // Current render width and height (depends on active fbo)
- Size render; // Framebuffer width and height (render area, including black bars if required)
- Point renderOffset; // Offset from render area (must be divided by 2)
+ Size screen; // Screen current width and height
+ Point position; // Window current position
+ Size previousScreen; // Screen previous width and height (required on fullscreen/borderless-windowed toggle)
+ Point previousPosition; // Window previous position (required on fullscreeen/borderless-windowed toggle)
+ Size render; // Screen framebuffer width and height
+ Point renderOffset; // Screen framebuffer render offset (Not required anymore?)
+ Size currentFbo; // Current framebuffer render width and height (depends on active render texture)
Size screenMin; // Screen minimum width and height (for resizable window)
Size screenMax; // Screen maximum width and height (for resizable window)
Matrix screenScale; // Matrix to scale screen (framebuffer rendering)
@@ -323,16 +315,17 @@ typedef struct CoreData {
struct {
struct {
int exitKey; // Default exit key
- char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state
- char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state
+ char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state
+ char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state
- // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially
- char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame
+ // 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
- 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;
@@ -348,8 +341,8 @@ typedef struct CoreData {
bool cursorLocked; // Track if cursor is locked (disabled)
bool cursorOnScreen; // Tracks if cursor is inside client area
- char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state
- char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state
+ char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state
+ char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state
Vector2 currentWheelMove; // Registers current mouse wheel variation
Vector2 previousWheelMove; // Registers previous mouse wheel variation
@@ -393,21 +386,10 @@ RLAPI const char *raylib_version = RAYLIB_VERSION; // raylib version exported s
CoreData CORE = { 0 }; // Global CORE state context
-// Flag to note GPU acceleration is available,
-// referenced from other modules to support GPU data loading
-// NOTE: Useful to allow Texture, RenderTexture, Font.texture, Mesh.vaoId/vboId, Shader loading
-bool isGpuReady = false;
-
#if defined(SUPPORT_SCREEN_CAPTURE)
static int screenshotCounter = 0; // Screenshots counter
#endif
-#if defined(SUPPORT_GIF_RECORDING)
-static unsigned int gifFrameCounter = 0; // GIF frames counter
-static bool gifRecording = false; // GIF recording state
-static MsfGifState gifState = { 0 }; // MSGIF context state
-#endif
-
#if defined(SUPPORT_AUTOMATION_EVENTS)
// Automation events type
typedef enum AutomationEventType {
@@ -441,7 +423,7 @@ typedef enum AutomationEventType {
} AutomationEventType;
// Event type to config events flags
-// TODO: Not used at the moment
+// WARNING: Not used at the moment
typedef enum {
EVENT_INPUT_KEYBOARD = 0,
EVENT_INPUT_MOUSE = 1,
@@ -510,7 +492,6 @@ extern int InitPlatform(void); // Initialize platform (graphics, inputs
extern void ClosePlatform(void); // Close platform
static void InitTimer(void); // Initialize timer, hi-resolution if available (required by InitPlatform())
-static void SetupFramebuffer(int width, int height); // Setup main framebuffer (required by InitPlatform())
static void SetupViewport(int width, int height); // Set viewport for a provided width and height
static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories in a base path
@@ -548,12 +529,6 @@ const char *TextFormat(const char *text, ...); // Formatting of text with variab
#if (!defined(SUPPORT_FILEFORMAT_PNG) || !defined(SUPPORT_FILEFORMAT_JPG)) && !defined(_WIN32)
#pragma message ("WARNING: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG")
#endif
-
- // Not needed because 'rtexture.c' will automatically defined STBI_REQUIRED when any SUPPORT_FILEFORMAT_* is defined
- // #if !defined(STBI_REQUIRED)
- // #pragma message ("WARNING: "STBI_REQUIRED is not defined, that means we can't load images from clipbard"
- // #endif
-
#endif // SUPPORT_CLIPBOARD_IMAGE
// Include platform-specific submodules
@@ -571,6 +546,8 @@ const char *TextFormat(const char *text, ...); // Formatting of text with variab
#include "platforms/rcore_drm.c"
#elif defined(PLATFORM_ANDROID)
#include "platforms/rcore_android.c"
+#elif defined(PLATFORM_MEMORY)
+ #include "platforms/rcore_memory.c"
#else
// TODO: Include your custom platform backend!
// i.e software rendering backend or console backend!
@@ -645,6 +622,8 @@ void InitWindow(int width, int height, const char *title)
TRACELOG(LOG_INFO, "Platform backend: NATIVE DRM");
#elif defined(PLATFORM_ANDROID)
TRACELOG(LOG_INFO, "Platform backend: ANDROID");
+#elif defined(PLATFORM_MEMORY)
+ TRACELOG(LOG_INFO, "Platform backend: MEMORY (No OS)");
#else
// TODO: Include your custom platform backend!
// i.e software rendering backend or console backend!
@@ -711,7 +690,6 @@ void InitWindow(int width, int height, const char *title)
// Initialize rlgl default data (buffers and shaders)
// NOTE: Current fbo size stored as globals in rlgl for convenience
rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
- isGpuReady = true; // Flag to note GPU has been initialized successfully
// Setup default viewport
SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
@@ -725,7 +703,7 @@ void InitWindow(int width, int height, const char *title)
// Set font white rectangle for shapes drawing, so shapes and text can be batched together
// WARNING: rshapes module is required, if not available, default internal white rectangle is used
Rectangle rec = GetFontDefault().recs[95];
- if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
{
// NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering
SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 });
@@ -758,15 +736,6 @@ void InitWindow(int width, int height, const char *title)
// Close window and unload OpenGL context
void CloseWindow(void)
{
-#if defined(SUPPORT_GIF_RECORDING)
- if (gifRecording)
- {
- MsfGifResult result = msf_gif_end(&gifState);
- msf_gif_free(result);
- gifRecording = false;
- }
-#endif
-
#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
UnloadFontDefault(); // WARNING: Module required: rtext
#endif
@@ -791,31 +760,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 ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0);
+ return FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN);
}
// Check if window has been minimized
bool IsWindowMinimized(void)
{
- return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0);
+ return FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
}
// Check if window has been maximized
bool IsWindowMaximized(void)
{
- return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0);
+ return FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
}
// Check if window has the focus
bool IsWindowFocused(void)
{
- return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0);
+ return !FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
}
// Check if window has been resizedLastFrame
@@ -827,7 +796,7 @@ bool IsWindowResized(void)
// Check if one specific window flag is enabled
bool IsWindowState(unsigned int flag)
{
- return ((CORE.Window.flags & flag) > 0);
+ return FLAG_IS_SET(CORE.Window.flags, flag);
}
// Get current screen width
@@ -845,30 +814,22 @@ int GetScreenHeight(void)
// Get current render width which is equal to screen width*dpi scale
int GetRenderWidth(void)
{
- if (CORE.Window.usingFbo) return CORE.Window.currentFbo.width;
-
int width = 0;
-#if defined(__APPLE__)
- Vector2 scale = GetWindowScaleDPI();
- width = (int)((float)CORE.Window.render.width*scale.x);
-#else
- width = CORE.Window.render.width;
-#endif
+
+ if (CORE.Window.usingFbo) return CORE.Window.currentFbo.width;
+ else width = CORE.Window.render.width;
+
return width;
}
// Get current screen height which is equal to screen height*dpi scale
int GetRenderHeight(void)
{
- if (CORE.Window.usingFbo) return CORE.Window.currentFbo.height;
-
int height = 0;
-#if defined(__APPLE__)
- Vector2 scale = GetWindowScaleDPI();
- height = (int)((float)CORE.Window.render.height*scale.y);
-#else
- height = CORE.Window.render.height;
-#endif
+
+ if (CORE.Window.usingFbo) return CORE.Window.currentFbo.height;
+ else height = CORE.Window.render.height;
+
return height;
}
@@ -929,47 +890,6 @@ void EndDrawing(void)
{
rlDrawRenderBatchActive(); // Update and draw internal render batch
-#if defined(SUPPORT_GIF_RECORDING)
- // Draw record indicator
- if (gifRecording)
- {
- #ifndef GIF_RECORD_FRAMERATE
- #define GIF_RECORD_FRAMERATE 10
- #endif
- gifFrameCounter += (unsigned int)(GetFrameTime()*1000);
-
- // NOTE: We record one gif frame depending on the desired gif framerate
- if (gifFrameCounter > 1000/GIF_RECORD_FRAMERATE)
- {
- // Get image data for the current frame (from backbuffer)
- // NOTE: This process is quite slow... :(
- Vector2 scale = GetWindowScaleDPI();
- unsigned char *screenData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
-
- #ifndef GIF_RECORD_BITRATE
- #define GIF_RECORD_BITRATE 16
- #endif
-
- // Add the frame to the gif recording, given how many frames have passed in centiseconds
- msf_gif_frame(&gifState, screenData, gifFrameCounter/10, GIF_RECORD_BITRATE, (int)((float)CORE.Window.render.width*scale.x)*4);
- gifFrameCounter -= 1000/GIF_RECORD_FRAMERATE;
-
- RL_FREE(screenData); // Free image data
- }
-
- #if defined(SUPPORT_MODULE_RSHAPES) && defined(SUPPORT_MODULE_RTEXT)
- // Display the recording indicator every half-second
- if ((int)(GetTime()/0.5)%2 == 1)
- {
- DrawCircle(30, CORE.Window.screen.height - 20, 10, MAROON); // WARNING: Module required: rshapes
- DrawText("GIF RECORDING", 50, CORE.Window.screen.height - 25, 10, RED); // WARNING: Module required: rtext
- }
- #endif
-
- rlDrawRenderBatchActive(); // Update and draw internal render batch
- }
-#endif
-
#if defined(SUPPORT_AUTOMATION_EVENTS)
if (automationEventRecording) RecordAutomationEvent(); // Event recording
#endif
@@ -1002,38 +922,8 @@ void EndDrawing(void)
#if defined(SUPPORT_SCREEN_CAPTURE)
if (IsKeyPressed(KEY_F12))
{
-#if defined(SUPPORT_GIF_RECORDING)
- if (IsKeyDown(KEY_LEFT_CONTROL))
- {
- if (gifRecording)
- {
- gifRecording = false;
-
- MsfGifResult result = msf_gif_end(&gifState);
-
- SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize);
- msf_gif_free(result);
-
- TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording");
- }
- else
- {
- gifRecording = true;
- gifFrameCounter = 0;
-
- Vector2 scale = GetWindowScaleDPI();
- msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
- screenshotCounter++;
-
- TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter));
- }
- }
- else
-#endif // SUPPORT_GIF_RECORDING
- {
- TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
- screenshotCounter++;
- }
+ TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
+ screenshotCounter++;
}
#endif // SUPPORT_SCREEN_CAPTURE
@@ -1208,7 +1098,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 && ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0))
+ 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));
@@ -1360,25 +1250,32 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
shader.id = rlLoadShaderCode(vsCode, fsCode);
- if (shader.id == rlGetShaderIdDefault()) shader.locs = rlGetShaderLocsDefault();
+ if (shader.id == 0)
+ {
+ // Shader could not be loaded but we still load the location points to avoid potential crashes
+ // NOTE: All locations set to -1 (no location)
+ shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
+ for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
+ }
+ else if (shader.id == rlGetShaderIdDefault()) shader.locs = rlGetShaderLocsDefault();
else if (shader.id > 0)
{
// After custom shader loading, we TRY to set default location names
// Default shader attribute locations have been binded before linking:
- // vertex position location = 0
- // vertex texcoord location = 1
- // vertex normal location = 2
- // vertex color location = 3
- // vertex tangent location = 4
- // vertex texcoord2 location = 5
- // vertex boneIds location = 6
- // vertex boneWeights location = 7
+ // - vertex position location = 0
+ // - vertex texcoord location = 1
+ // - vertex normal location = 2
+ // - vertex color location = 3
+ // - vertex tangent location = 4
+ // - vertex texcoord2 location = 5
+ // - vertex boneIds location = 6
+ // - vertex boneWeights location = 7
// NOTE: If any location is not found, loc point becomes -1
+ // Load shader locations array
+ // NOTE: All locations set to -1 (no location)
shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
-
- // All locations reset to -1 (no location)
for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
// Get handles to GLSL input attribute locations
@@ -1641,8 +1538,6 @@ Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int heigh
// Calculate view matrix from camera look at (and transpose it)
Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
- // TODO: Why not use Vector3Transform(Vector3 v, Matrix mat)?
-
// Convert world position vector to quaternion
Quaternion worldPos = { position.x, position.y, position.z, 1.0f };
@@ -1755,8 +1650,8 @@ float GetFrameTime(void)
// Wait for some time (stop program execution)
// NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could
// take longer than expected... for that reason we use the busy wait loop
-// Ref: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected
-// Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32!
+// REF: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected
+// REF: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32!
void WaitTime(double seconds)
{
if (seconds < 0) return; // Security check
@@ -1838,8 +1733,33 @@ 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
+ //value = (rand()%(abs(max - min) + 1) + min);
- value = (rand()%(abs(max - min) + 1) + min);
+ // More uniform range solution
+ int range = (max - min) + 1;
+
+ // Degenerate/overflow case: fall back to min (same behavior as "always min" instead of UB)
+ if (range <= 0) value = min;
+ else
+ {
+ // Rejection sampling to get a uniform integer in [min, max]
+ unsigned long c = (unsigned long)RAND_MAX + 1UL; // number of possible rand() results
+ unsigned long m = (unsigned long)range; // size of the target interval
+ unsigned long t = c - (c%m); // largest multiple of m <= c
+ unsigned long r = 0;
+
+ for (;;)
+ {
+ r = (unsigned long)rand();
+ if (r < t) break; // Only accept values within the fair region
+ }
+
+ value = min + (int)(r%m);
+ }
#endif
return value;
}
@@ -1861,7 +1781,7 @@ int *LoadRandomSequence(unsigned int count, int min, int max)
for (int i = 0; i < (int)count;)
{
- value = (rand()%(abs(max - min) + 1) + min);
+ value = GetRandomValue(min, max);
dupValue = false;
for (int j = 0; j < i; j++)
@@ -1903,13 +1823,13 @@ void TakeScreenshot(const char *fileName)
// Apply a scale if we are doing HIGHDPI auto-scaling
Vector2 scale = { 1.0f, 1.0f };
- if (IsWindowState(FLAG_WINDOW_HIGHDPI)) scale = GetWindowScaleDPI();
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) scale = GetWindowScaleDPI();
unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
- char path[512] = { 0 };
- strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName));
+ char path[MAX_FILEPATH_LENGTH] = { 0 };
+ strncpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName), MAX_FILEPATH_LENGTH - 1);
ExportImage(image, path); // WARNING: Module required: rtextures
RL_FREE(imgData);
@@ -1931,7 +1851,7 @@ void SetConfigFlags(unsigned int flags)
// Selected flags are set but not evaluated at this point,
// flag evaluation happens at InitWindow() or SetWindowState()
- CORE.Window.flags |= flags;
+ FLAG_SET(CORE.Window.flags, flags);
}
//----------------------------------------------------------------------------------
@@ -2079,10 +1999,10 @@ bool IsFileExtension(const char *fileName, const char *ext)
if (fileExt != NULL)
{
- int fileExtLen = (int)strlen(fileExt);
+ int fileExtLength = (int)strlen(fileExt);
char fileExtLower[16] = { 0 };
char *fileExtLowerPtr = fileExtLower;
- for (int i = 0; (i < fileExtLen) && (i < 16); i++)
+ for (int i = 0; (i < fileExtLength) && (i < 16); i++)
{
// Copy and convert to lower-case
if ((fileExt[i] >= 'A') && (fileExt[i] <= 'Z')) fileExtLower[i] = fileExt[i] + 32;
@@ -2090,13 +2010,13 @@ bool IsFileExtension(const char *fileName, const char *ext)
}
int extCount = 1;
- int extLen = (int)strlen(ext);
- char *extList = (char *)RL_CALLOC(extLen + 1, 1);
+ int extLength = (int)strlen(ext);
+ char *extList = (char *)RL_CALLOC(extLength + 1, 1);
char *extListPtrs[MAX_FILE_EXTENSIONS] = { 0 };
- strcpy(extList, ext);
+ strncpy(extList, ext, extLength);
extListPtrs[0] = extList;
- for (int i = 0; i < extLen; i++)
+ for (int i = 0; i < extLength; i++)
{
// Convert to lower-case if extension is upper-case
if ((extList[i] >= 'A') && (extList[i] <= 'Z')) extList[i] += 32;
@@ -2201,11 +2121,11 @@ const char *GetFileExtension(const char *fileName)
}
// String pointer reverse break: returns right-most occurrence of charset in s
-static const char *strprbrk(const char *s, const char *charset)
+static const char *strprbrk(const char *text, const char *charset)
{
const char *latestMatch = NULL;
- for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { }
+ for (; (text != NULL) && (text = strpbrk(text, charset)); latestMatch = text++) { }
return latestMatch;
}
@@ -2232,10 +2152,10 @@ const char *GetFileNameWithoutExt(const char *filePath)
if (filePath != NULL)
{
- strcpy(fileName, GetFileName(filePath)); // Get filename.ext without path
- int size = (int)strlen(fileName); // Get size in bytes
+ strncpy(fileName, GetFileName(filePath), MAX_FILENAME_LENGTH - 1); // Get filename.ext without path
+ int fileNameLenght = (int)strlen(fileName); // Get size in bytes
- for (int i = size; i > 0; i--) // Reverse search '.'
+ for (int i = fileNameLenght; i > 0; i--) // Reverse search '.'
{
if (fileName[i] == '.')
{
@@ -2302,11 +2222,11 @@ const char *GetPrevDirectoryPath(const char *dirPath)
{
static char prevDirPath[MAX_FILEPATH_LENGTH] = { 0 };
memset(prevDirPath, 0, MAX_FILEPATH_LENGTH);
- int pathLen = (int)strlen(dirPath);
+ int dirPathLength = (int)strlen(dirPath);
- if (pathLen <= 3) strcpy(prevDirPath, dirPath);
+ if (dirPathLength <= 3) strncpy(prevDirPath, dirPath, MAX_FILEPATH_LENGTH - 1);
- for (int i = (pathLen - 1); (i >= 0) && (pathLen > 3); i--)
+ for (int i = (dirPathLength - 1); (i >= 0) && (dirPathLength > 3); i--)
{
if ((dirPath[i] == '\\') || (dirPath[i] == '/'))
{
@@ -2339,13 +2259,15 @@ const char *GetApplicationDirectory(void)
#if defined(_WIN32)
int len = 0;
-#if defined(UNICODE)
+
+ #if defined(UNICODE)
unsigned short widePath[MAX_PATH];
len = GetModuleFileNameW(NULL, (wchar_t *)widePath, MAX_PATH);
len = WideCharToMultiByte(0, 0, (wchar_t *)widePath, len, appDir, MAX_PATH, NULL, NULL);
-#else
+ #else
len = GetModuleFileNameA(NULL, appDir, MAX_PATH);
-#endif
+ #endif
+
if (len > 0)
{
for (int i = len; i >= 0; --i)
@@ -2362,8 +2284,9 @@ const char *GetApplicationDirectory(void)
appDir[0] = '.';
appDir[1] = '\\';
}
-
+
#elif defined(__linux__)
+
unsigned int size = sizeof(appDir);
ssize_t len = readlink("/proc/self/exe", appDir, size);
@@ -2383,13 +2306,15 @@ const char *GetApplicationDirectory(void)
appDir[0] = '.';
appDir[1] = '/';
}
+
#elif defined(__APPLE__)
+
uint32_t size = sizeof(appDir);
if (_NSGetExecutablePath(appDir, &size) == 0)
{
- int len = strlen(appDir);
- for (int i = len; i >= 0; --i)
+ int appDirLength = (int)strlen(appDir);
+ for (int i = appDirLength; i >= 0; --i)
{
if (appDir[i] == '/')
{
@@ -2403,14 +2328,16 @@ const char *GetApplicationDirectory(void)
appDir[0] = '.';
appDir[1] = '/';
}
+
#elif defined(__FreeBSD__)
+
size_t size = sizeof(appDir);
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
if (sysctl(mib, 4, appDir, &size, NULL, 0) == 0)
{
- int len = strlen(appDir);
- for (int i = len; i >= 0; --i)
+ int appDirLength = (int)strlen(appDir);
+ for (int i = appDirLength; i >= 0; --i)
{
if (appDir[i] == '/')
{
@@ -2424,7 +2351,6 @@ const char *GetApplicationDirectory(void)
appDir[0] = '.';
appDir[1] = '/';
}
-
#endif
return appDir;
@@ -2506,12 +2432,12 @@ int MakeDirectory(const char *dirPath)
if (DirectoryExists(dirPath)) return 0; // Path already exists (is valid)
// Copy path string to avoid modifying original
- int len = (int)strlen(dirPath) + 1;
- char *pathcpy = (char *)RL_CALLOC(len, 1);
- memcpy(pathcpy, dirPath, len);
+ int dirPathLength = (int)strlen(dirPath) + 1;
+ char *pathcpy = (char *)RL_CALLOC(dirPathLength, 1);
+ memcpy(pathcpy, dirPath, dirPathLength);
// Iterate over pathcpy, create each subdirectory as needed
- for (int i = 0; (i < len) && (pathcpy[i] != '\0'); i++)
+ for (int i = 0; (i < dirPathLength) && (pathcpy[i] != '\0'); i++)
{
if (pathcpy[i] == ':') i++;
else
@@ -2537,12 +2463,12 @@ int MakeDirectory(const char *dirPath)
}
// Change working directory, returns true on success
-bool ChangeDirectory(const char *dir)
+bool ChangeDirectory(const char *dirPath)
{
- bool result = CHDIR(dir);
+ bool result = CHDIR(dirPath);
- if (result != 0) TRACELOG(LOG_WARNING, "SYSTEM: Failed to change to directory: %s", dir);
- else TRACELOG(LOG_INFO, "SYSTEM: Working Directory: %s", dir);
+ if (result != 0) TRACELOG(LOG_WARNING, "SYSTEM: Failed to change to directory: %s", dirPath);
+ else TRACELOG(LOG_INFO, "SYSTEM: Working Directory: %s", dirPath);
return (result == 0);
}
@@ -2563,10 +2489,10 @@ bool IsFileNameValid(const char *fileName)
if ((fileName != NULL) && (fileName[0] != '\0'))
{
- int length = (int)strlen(fileName);
+ int fileNameLength = (int)strlen(fileName);
bool allPeriods = true;
- for (int i = 0; i < length; i++)
+ for (int i = 0; i < fileNameLength; i++)
{
// Check invalid characters
if ((fileName[i] == '<') ||
@@ -2582,8 +2508,6 @@ bool IsFileNameValid(const char *fileName)
// Check non-glyph characters
if ((unsigned char)fileName[i] < 32) { valid = false; break; }
- // TODO: Check trailing periods/spaces?
-
// Check if filename is not all periods
if (fileName[i] != '.') allPeriods = false;
}
@@ -2594,7 +2518,7 @@ bool IsFileNameValid(const char *fileName)
if (valid)
{
// Check invalid DOS names
- if (length >= 3)
+ if (fileNameLength >= 3)
{
if (((fileName[0] == 'C') && (fileName[1] == 'O') && (fileName[2] == 'N')) || // CON
((fileName[0] == 'P') && (fileName[1] == 'R') && (fileName[2] == 'N')) || // PRN
@@ -2602,7 +2526,7 @@ bool IsFileNameValid(const char *fileName)
((fileName[0] == 'N') && (fileName[1] == 'U') && (fileName[2] == 'L'))) valid = false; // NUL
}
- if (length >= 4)
+ if (fileNameLength >= 4)
{
if (((fileName[0] == 'C') && (fileName[1] == 'O') && (fileName[2] == 'M') && ((fileName[3] >= '0') && (fileName[3] <= '9'))) || // COM0-9
((fileName[0] == 'L') && (fileName[1] == 'P') && (fileName[2] == 'T') && ((fileName[3] >= '0') && (fileName[3] <= '9')))) valid = false; // LPT0-9
@@ -2775,6 +2699,9 @@ 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;
// Compute expected size and padding
int dataSize = (int)strlen(text); // WARNING: Expecting NULL terminated strings!
@@ -2992,7 +2919,7 @@ unsigned int *ComputeMD5(unsigned char *data, int dataSize)
// NOTE: Returns a static int[5] array (20 bytes)
unsigned int *ComputeSHA1(unsigned char *data, int dataSize)
{
- #define ROTATE_LEFT(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
+ #define SHA1_ROTATE_LEFT(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
static unsigned int hash[5] = { 0 }; // Hash to be returned
@@ -3018,8 +2945,15 @@ unsigned int *ComputeSHA1(unsigned char *data, int dataSize)
memcpy(msg, data, dataSize);
msg[dataSize] = 128; // Write the '1' bit
- unsigned int bitsLen = 8*dataSize;
- msg[newDataSize-1] = bitsLen;
+ unsigned long long bitsLen = 8ULL * dataSize;
+ msg[newDataSize-1] = (unsigned char)(bitsLen);
+ msg[newDataSize-2] = (unsigned char)(bitsLen >> 8);
+ msg[newDataSize-3] = (unsigned char)(bitsLen >> 16);
+ msg[newDataSize-4] = (unsigned char)(bitsLen >> 24);
+ msg[newDataSize-5] = (unsigned char)(bitsLen >> 32);
+ msg[newDataSize-6] = (unsigned char)(bitsLen >> 40);
+ msg[newDataSize-7] = (unsigned char)(bitsLen >> 48);
+ msg[newDataSize-8] = (unsigned char)(bitsLen >> 56);
// Process the message in successive 512-bit chunks
for (int offset = 0; offset < newDataSize; offset += (512/8))
@@ -3035,7 +2969,7 @@ unsigned int *ComputeSHA1(unsigned char *data, int dataSize)
}
// Message schedule: extend the sixteen 32-bit words into eighty 32-bit words:
- for (int i = 16; i < 80; i++) w[i] = ROTATE_LEFT(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);
+ for (int i = 16; i < 80; i++) w[i] = SHA1_ROTATE_LEFT(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);
// Initialize hash value for this chunk
unsigned int a = hash[0];
@@ -3070,10 +3004,10 @@ unsigned int *ComputeSHA1(unsigned char *data, int dataSize)
k = 0xCA62C1D6;
}
- unsigned int temp = ROTATE_LEFT(a, 5) + f + e + k + w[i];
+ unsigned int temp = SHA1_ROTATE_LEFT(a, 5) + f + e + k + w[i];
e = d;
d = c;
- c = ROTATE_LEFT(b, 30);
+ c = SHA1_ROTATE_LEFT(b, 30);
b = a;
a = temp;
}
@@ -3095,9 +3029,9 @@ unsigned int *ComputeSHA1(unsigned char *data, int dataSize)
// NOTE: Returns a static int[8] array (32 bytes)
unsigned int *ComputeSHA256(unsigned char *data, int dataSize)
{
- #define ROTATE_RIGHT(x, c) ((x >> c) | (x << ((sizeof(unsigned int) * 8) - c)))
- #define SHA256_A0(x) (ROTATE_RIGHT(x, 7) ^ ROTATE_RIGHT(x, 18) ^ (x >> 3))
- #define SHA256_A1(x) (ROTATE_RIGHT(x, 17) ^ ROTATE_RIGHT(x, 19) ^ (x >> 10))
+ #define SHA256_ROTATE_RIGHT(x, c) ((x >> c) | (x << ((sizeof(unsigned int)*8) - c)))
+ #define SHA256_A0(x) (SHA256_ROTATE_RIGHT(x, 7) ^ SHA256_ROTATE_RIGHT(x, 18) ^ (x >> 3))
+ #define SHA256_A1(x) (SHA256_ROTATE_RIGHT(x, 17) ^ SHA256_ROTATE_RIGHT(x, 19) ^ (x >> 10))
static const unsigned int k[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
@@ -3118,7 +3052,7 @@ unsigned int *ComputeSHA256(unsigned char *data, int dataSize)
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
- static unsigned int hash[8];
+ static unsigned int hash[8] = { 0 };
hash[0] = 0x6A09e667;
hash[1] = 0xbb67ae85;
hash[2] = 0x3c6ef372;
@@ -3131,12 +3065,14 @@ unsigned int *ComputeSHA256(unsigned char *data, int dataSize)
const unsigned long long int bitLen = ((unsigned long long int)dataSize)*8;
unsigned long long int paddedSize = dataSize + sizeof(dataSize);
paddedSize += (64 - (paddedSize%64));
- unsigned char *buffer = RL_CALLOC(paddedSize, sizeof(unsigned char));
+ unsigned char *buffer = (unsigned char *)RL_CALLOC(paddedSize, sizeof(unsigned char));
memcpy(buffer, data, dataSize);
buffer[dataSize] = 0x80;
for (int i = 1; i <= sizeof(bitLen); i++)
+ {
buffer[(paddedSize - sizeof(bitLen)) + (i - 1)] = (bitLen >> (8*(sizeof(bitLen) - i))) & 0xFF;
+ }
for (unsigned long long int blockN = 0; blockN < paddedSize/64; blockN++)
{
@@ -3150,23 +3086,22 @@ unsigned int *ComputeSHA256(unsigned char *data, int dataSize)
unsigned int h = hash[7];
unsigned char *block = buffer + (blockN*64);
- unsigned int w[64];
+ unsigned int w[64] = { 0 };
for (int i = 0; i < 16; i++)
{
- w[i] =
- ((unsigned int)block[i*4 + 0] << 24) |
- ((unsigned int)block[i*4 + 1] << 16) |
- ((unsigned int)block[i*4 + 2] << 8) |
- ((unsigned int)block[i*4 + 3]);
+ w[i] = ((unsigned int)block[i*4 + 0] << 24) |
+ ((unsigned int)block[i*4 + 1] << 16) |
+ ((unsigned int)block[i*4 + 2] << 8) |
+ ((unsigned int)block[i*4 + 3]);
}
for (int t = 16; t < 64; t++) w[t] = SHA256_A1(w[t - 2]) + w[t - 7] + SHA256_A0(w[t - 15]) + w[t - 16];
for (unsigned long long int t = 0; t < 64; t++)
{
- unsigned int e1 = (ROTATE_RIGHT(e, 6) ^ ROTATE_RIGHT(e, 11) ^ ROTATE_RIGHT(e, 25));
+ unsigned int e1 = (SHA256_ROTATE_RIGHT(e, 6) ^ SHA256_ROTATE_RIGHT(e, 11) ^ SHA256_ROTATE_RIGHT(e, 25));
unsigned int ch = ((e & f) ^ (~e & g));
unsigned int t1 = (h + e1 + ch + k[t] + w[t]);
- unsigned int e0 = (ROTATE_RIGHT(a, 2) ^ ROTATE_RIGHT(a, 13) ^ ROTATE_RIGHT(a, 22));
+ unsigned int e0 = (SHA256_ROTATE_RIGHT(a, 2) ^ SHA256_ROTATE_RIGHT(a, 13) ^ SHA256_ROTATE_RIGHT(a, 22));
unsigned int maj = ((a & b) ^ (a & c) ^ (b & c));
unsigned int t2 = e0 + maj;
@@ -3189,7 +3124,9 @@ unsigned int *ComputeSHA256(unsigned char *data, int dataSize)
hash[6] += g;
hash[7] += h;
}
+
RL_FREE(buffer);
+
return hash;
}
@@ -3305,7 +3242,7 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName)
*/
// Export events as text
- // TODO: Save to memory buffer and SaveFileText()
+ // NOTE: Save to memory buffer and SaveFileText()
char *txtData = (char *)RL_CALLOC(256*list.count + 2048, sizeof(char)); // 256 characters per line plus some header
int byteCount = 0;
@@ -3374,7 +3311,7 @@ void PlayAutomationEvent(AutomationEvent event)
#if defined(SUPPORT_AUTOMATION_EVENTS)
// WARNING: When should event be played? After/before/replace PollInputEvents()? -> Up to the user!
- if (!automationEventRecording) // TODO: Allow recording events while playing?
+ if (!automationEventRecording)
{
switch (event.type)
{
@@ -3811,7 +3748,6 @@ int GetTouchY(void)
}
// Get touch position XY for a touch point index (relative to screen size)
-// TODO: Touch position should be scaled depending on display size and render size
Vector2 GetTouchPosition(int index)
{
Vector2 position = { -1.0f, -1.0f };
@@ -3854,20 +3790,20 @@ void InitTimer(void)
// High resolutions can also prevent the CPU power management system from entering power-saving modes
// Setting a higher resolution does not improve the accuracy of the high-resolution performance counter
#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) && !defined(PLATFORM_DESKTOP_SDL)
- timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
+ timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
#endif
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
struct timespec now = { 0 };
- if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success
+ if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success
{
CORE.Time.base = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec;
}
else TRACELOG(LOG_WARNING, "TIMER: Hi-resolution timer not available");
#endif
- CORE.Time.previous = GetTime(); // Get time as double
+ CORE.Time.previous = GetTime(); // Get time as double
}
// Set viewport for a provided width and height
@@ -3877,14 +3813,7 @@ void SetupViewport(int width, int height)
CORE.Window.render.height = height;
// Set viewport width and height
- // NOTE: We consider render size (scaled) and offset in case black bars are required and
- // render area does not match full display area (this situation is only applicable on fullscreen mode)
-#if defined(__APPLE__)
- Vector2 scale = GetWindowScaleDPI();
- rlViewport(CORE.Window.renderOffset.x/2*scale.x, CORE.Window.renderOffset.y/2*scale.y, (CORE.Window.render.width)*scale.x, (CORE.Window.render.height)*scale.y);
-#else
rlViewport(CORE.Window.renderOffset.x/2, CORE.Window.renderOffset.y/2, CORE.Window.render.width, CORE.Window.render.height);
-#endif
rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
rlLoadIdentity(); // Reset current matrix (projection)
@@ -3897,84 +3826,6 @@ void SetupViewport(int width, int height)
rlLoadIdentity(); // Reset current matrix (modelview)
}
-// Compute framebuffer size relative to screen size and display size
-// NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified
-void SetupFramebuffer(int width, int height)
-{
- // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var)
- if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height))
- {
- TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
-
- // Downscaling to fit display with border-bars
- float widthRatio = (float)CORE.Window.display.width/(float)CORE.Window.screen.width;
- float heightRatio = (float)CORE.Window.display.height/(float)CORE.Window.screen.height;
-
- if (widthRatio <= heightRatio)
- {
- CORE.Window.render.width = CORE.Window.display.width;
- CORE.Window.render.height = (int)round((float)CORE.Window.screen.height*widthRatio);
- CORE.Window.renderOffset.x = 0;
- CORE.Window.renderOffset.y = (CORE.Window.display.height - CORE.Window.render.height);
- }
- else
- {
- CORE.Window.render.width = (int)round((float)CORE.Window.screen.width*heightRatio);
- CORE.Window.render.height = CORE.Window.display.height;
- CORE.Window.renderOffset.x = (CORE.Window.display.width - CORE.Window.render.width);
- CORE.Window.renderOffset.y = 0;
- }
-
- // Screen scaling required
- float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width;
- CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f);
-
- // NOTE: We render to full display resolution!
- // We just need to calculate above parameters for downscale matrix and offsets
- CORE.Window.render.width = CORE.Window.display.width;
- CORE.Window.render.height = CORE.Window.display.height;
-
- TRACELOG(LOG_WARNING, "DISPLAY: Downscale matrix generated, content will be rendered at (%ix%i)", CORE.Window.render.width, CORE.Window.render.height);
- }
- else if ((CORE.Window.screen.width < CORE.Window.display.width) || (CORE.Window.screen.height < CORE.Window.display.height))
- {
- // Required screen size is smaller than display size
- TRACELOG(LOG_INFO, "DISPLAY: Upscaling required: Screen size (%ix%i) smaller than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
-
- if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0))
- {
- CORE.Window.screen.width = CORE.Window.display.width;
- CORE.Window.screen.height = CORE.Window.display.height;
- }
-
- // Upscaling to fit display with border-bars
- float displayRatio = (float)CORE.Window.display.width/(float)CORE.Window.display.height;
- float screenRatio = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
-
- if (displayRatio <= screenRatio)
- {
- CORE.Window.render.width = CORE.Window.screen.width;
- CORE.Window.render.height = (int)round((float)CORE.Window.screen.width/displayRatio);
- CORE.Window.renderOffset.x = 0;
- CORE.Window.renderOffset.y = (CORE.Window.render.height - CORE.Window.screen.height);
- }
- else
- {
- CORE.Window.render.width = (int)round((float)CORE.Window.screen.height*displayRatio);
- CORE.Window.render.height = CORE.Window.screen.height;
- CORE.Window.renderOffset.x = (CORE.Window.render.width - CORE.Window.screen.width);
- CORE.Window.renderOffset.y = 0;
- }
- }
- else
- {
- CORE.Window.render.width = CORE.Window.screen.width;
- CORE.Window.render.height = CORE.Window.screen.height;
- CORE.Window.renderOffset.x = 0;
- CORE.Window.renderOffset.y = 0;
- }
-}
-
// Scan all files and directories in a base path
// WARNING: files.paths[] must be previously allocated and
// contain enough space to store all required paths
@@ -3993,6 +3844,7 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const
if ((strcmp(dp->d_name, ".") != 0) &&
(strcmp(dp->d_name, "..") != 0))
{
+ // Construct new path from our base path
#if defined(_WIN32)
int pathLength = snprintf(path, MAX_FILEPATH_LENGTH - 1, "%s\\%s", basePath, dp->d_name);
#else
@@ -4009,7 +3861,7 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const
{
if (IsFileExtension(path, filter))
{
- strcpy(files->paths[files->count], path);
+ strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
files->count++;
}
}
@@ -4017,14 +3869,14 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const
{
if (strstr(filter, DIRECTORY_FILTER_TAG) != NULL)
{
- strcpy(files->paths[files->count], path);
+ strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
files->count++;
}
}
}
else
{
- strcpy(files->paths[files->count], path);
+ strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
files->count++;
}
}
@@ -4068,13 +3920,13 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi
{
if (IsFileExtension(path, filter))
{
- strcpy(files->paths[files->count], path);
+ strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
files->count++;
}
}
else
{
- strcpy(files->paths[files->count], path);
+ strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
files->count++;
}
@@ -4088,7 +3940,7 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi
{
if ((filter != NULL) && (strstr(filter, DIRECTORY_FILTER_TAG) != NULL))
{
- strcpy(files->paths[files->count], path);
+ strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
files->count++;
}
@@ -4110,13 +3962,11 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi
#if defined(SUPPORT_AUTOMATION_EVENTS)
// Automation event recording
+// Checking events in current frame and save them into currentEventList
// NOTE: Recording is by default done at EndDrawing(), before PollInputEvents()
static void RecordAutomationEvent(void)
{
- // Checking events in current frame and save them into currentEventList
- // TODO: How important is the current frame? Could it be modified?
-
- if (currentEventList->count == currentEventList->capacity) return; // Security check
+ if (currentEventList->count == currentEventList->capacity) return;
// Keyboard input events recording
//-------------------------------------------------------------------------------------
@@ -4393,22 +4243,25 @@ const char *TextFormat(const char *text, ...)
char *currentBuffer = buffers[index];
memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using
-
- va_list args;
- va_start(args, text);
- int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
- va_end(args);
-
- // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured
- if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
+
+ if (text != NULL)
{
- // Inserting "..." at the end of the string to mark as truncated
- char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
- sprintf(truncBuffer, "...");
- }
+ va_list args;
+ va_start(args, text);
+ int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
+ va_end(args);
- index += 1; // Move to next buffer for next function call
- if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
+ // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occurred
+ if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
+ {
+ // Inserting "..." at the end of the string to mark as truncated
+ char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
+ snprintf(truncBuffer, 4, "...");
+ }
+
+ index += 1; // Move to next buffer for next function call
+ if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
+ }
return currentBuffer;
}
diff --git a/src/rlgl.h b/src/rlgl.h
index e6a1c9432..cda64896c 100644
--- a/src/rlgl.h
+++ b/src/rlgl.h
@@ -21,6 +21,7 @@
* Internal buffer (and resources) must be manually unloaded calling rlglClose()
*
* CONFIGURATION:
+* #define GRAPHICS_API_OPENGL_11_SOFTWARE
* #define GRAPHICS_API_OPENGL_11
* #define GRAPHICS_API_OPENGL_21
* #define GRAPHICS_API_OPENGL_33
@@ -775,10 +776,9 @@ RLAPI unsigned int rlLoadFramebuffer(void); // Loa
RLAPI void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel); // Attach texture/renderbuffer to a framebuffer
RLAPI bool rlFramebufferComplete(unsigned int id); // Verify framebuffer is complete
RLAPI void rlUnloadFramebuffer(unsigned int id); // Delete framebuffer from GPU
-#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+// WARNING: Copy and resize framebuffer functionality only defined for software backend
RLAPI void rlCopyFramebuffer(int x, int y, int width, int height, int format, void *pixels); // Copy framebuffer pixel data to internal buffer
RLAPI void rlResizeFramebuffer(int width, int height); // Resize internal framebuffer
-#endif
// Shaders management
RLAPI unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode); // Load shader from code strings
@@ -902,6 +902,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
// It seems OpenGL ES 2.0 instancing entry points are not defined on Raspberry Pi
// provided headers (despite being defined in official Khronos GLES2 headers)
+ // TODO: Avoid raylib platform-dependant code on rlgl, it should be a completely portable library
#if defined(PLATFORM_DRM)
typedef void (GL_APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount);
typedef void (GL_APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount);
@@ -1152,6 +1153,7 @@ static double rlCullDistanceFar = RL_CULL_DISTANCE_FAR;
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
static rlglData RLGL = { 0 };
#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
+static bool isGpuReady = false;
#if defined(GRAPHICS_API_OPENGL_ES2) && !defined(GRAPHICS_API_OPENGL_ES3)
// NOTE: VAO functionality is exposed through extensions (OES)
@@ -1251,7 +1253,7 @@ void rlPushMatrix(void)
RLGL.State.stackCounter++;
}
-// Pop lattest inserted matrix from RLGL.State.stack
+// Pop latest inserted matrix from RLGL.State.stack
void rlPopMatrix(void)
{
if (RLGL.State.stackCounter > 0)
@@ -1760,11 +1762,6 @@ void rlTextureParameters(unsigned int id, int param, int value)
{
glBindTexture(GL_TEXTURE_2D, id);
-#if !defined(GRAPHICS_API_OPENGL_11)
- // Reset anisotropy filter, in case it was set
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
-#endif
-
switch (param)
{
case RL_TEXTURE_WRAP_S:
@@ -1784,6 +1781,9 @@ void rlTextureParameters(unsigned int id, int param, int value)
case RL_TEXTURE_FILTER_ANISOTROPIC:
{
#if !defined(GRAPHICS_API_OPENGL_11)
+ // Reset anisotropy filter, in case it was set
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
+
if (value <= RLGL.ExtSupported.maxAnisotropyLevel) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value);
else if (RLGL.ExtSupported.maxAnisotropyLevel > 0.0f)
{
@@ -2282,6 +2282,8 @@ static void GLAPIENTRY rlDebugMessageCallback(GLenum source, GLenum type, GLuint
// Initialize rlgl: OpenGL extensions, default buffers/shaders/textures, OpenGL states
void rlglInit(int width, int height)
{
+ isGpuReady = true;
+
// Enable OpenGL debug context if required
#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) && defined(GRAPHICS_API_OPENGL_43)
if ((glDebugMessageCallback != NULL) && (glDebugMessageControl != NULL))
@@ -2394,6 +2396,7 @@ void rlglClose(void)
#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
swClose(); // Unload sofware renderer resources
#endif
+ isGpuReady = false;
}
// Load OpenGL extensions
@@ -2491,12 +2494,12 @@ void rlLoadExtensions(void *loader)
const char *extensions = (const char *)glGetString(GL_EXTENSIONS); // One big const string
// NOTE: We have to duplicate string because glGetString() returns a const string
- int size = strlen(extensions) + 1; // Get extensions string size in bytes
- char *extensionsDup = (char *)RL_CALLOC(size, sizeof(char));
- strcpy(extensionsDup, extensions);
+ int extensionsLength = (int)strlen(extensions); // Get extensions string size in bytes
+ char *extensionsDup = (char *)RL_CALLOC(extensionsLength + 1, sizeof(char)); // Allocate space for copy with additional EOL byte
+ strncpy(extensionsDup, extensions, extensionsLength);
extList[numExt] = extensionsDup;
- for (int i = 0; i < size; i++)
+ for (int i = 0; i < extensionsLength; i++)
{
if (extensionsDup[i] == ' ')
{
@@ -2798,6 +2801,7 @@ int *rlGetShaderLocsDefault(void)
rlRenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements)
{
rlRenderBatch batch = { 0 };
+ if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return batch; }
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
// Initialize CPU (RAM) vertex buffers (position, texcoord, color data and indexes)
@@ -2920,7 +2924,7 @@ rlRenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements)
batch.bufferCount = numBuffers; // Record buffer count
batch.drawCounter = 1; // Reset draws counter
- batch.currentDepth = -1.0f; // Reset depth value
+ batch.currentDepth = -1.0f; // Reset depth value
//--------------------------------------------------------------------------------------------
#endif
@@ -2981,7 +2985,8 @@ void rlDrawRenderBatch(rlRenderBatch *batch)
// Update batch vertex buffers
//------------------------------------------------------------------------------------------------------------
// NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0)
- // TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (use a change detector flag?)
+ // TODO: If no data changed on the CPU arrays there is no need to re-upload data to GPU,
+ // a flag can be used to detect changes but it would imply keeping a copy buffer and memcmp() both, does it worth it?
if (RLGL.State.vertexCounter > 0)
{
// Activate elements VAO
@@ -3251,6 +3256,7 @@ bool rlCheckRenderBatchLimit(int vCount)
unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount)
{
unsigned int id = 0;
+ if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; }
glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding
@@ -3347,8 +3353,8 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format,
mipWidth /= 2;
mipHeight /= 2;
- mipOffset += mipSize; // Increment offset position to next mipmap
- if (data != NULL) dataPtr += mipSize; // Increment data pointer to next mipmap
+ mipOffset += mipSize; // Increment offset position to next mipmap
+ if (data != NULL) dataPtr += mipSize; // Increment data pointer to next mipmap
// Security check for NPOT textures
if (mipWidth < 1) mipWidth = 1;
@@ -3382,10 +3388,18 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format,
#if defined(GRAPHICS_API_OPENGL_33)
if (mipmapCount > 1)
{
- // Activate Trilinear filtering if mipmaps are available
+ // 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);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipmapCount); // Required for user-defined mip count
+
+ // 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);
+
+ // Check if the loaded texture with mipmaps is complete,
+ // uncomplete textures will draw in black if mipmap filtering is required
+ //GLint complete = 0;
+ //glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_IMMUTABLE_FORMAT, &complete);
}
#endif
@@ -3407,6 +3421,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format,
unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer)
{
unsigned int id = 0;
+ if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; }
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
// In case depth textures not supported, we force renderbuffer usage
@@ -3465,6 +3480,7 @@ unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer)
unsigned int rlLoadTextureCubemap(const void *data, int size, int format, int mipmapCount)
{
unsigned int id = 0;
+ if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; }
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
int mipSize = size;
@@ -3747,21 +3763,23 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format)
return pixels;
}
-#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
// Copy framebuffer pixel data to internal buffer
void rlCopyFramebuffer(int x, int y, int width, int height, int format, void *pixels)
{
+#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
unsigned int glInternalFormat, glFormat, glType;
rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); // Get OpenGL texture format
swCopyFramebuffer(x, y, width, height, glFormat, glType, pixels);
+#endif
}
// Resize internal framebuffer
void rlResizeFramebuffer(int width, int height)
{
+#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
swResizeFramebuffer(width, height);
-}
#endif
+}
// Read screen pixel data (color buffer)
unsigned char *rlReadScreenPixels(int width, int height)
@@ -3807,6 +3825,7 @@ unsigned char *rlReadScreenPixels(int width, int height)
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)
glGenFramebuffers(1, &fboId); // Create the framebuffer object
@@ -3899,7 +3918,7 @@ void rlUnloadFramebuffer(unsigned int id)
// TODO: Review warning retrieving object name in WebGL
// WARNING: WebGL: INVALID_ENUM: getFramebufferAttachmentParameter: invalid parameter name
- // https://registry.khronos.org/webgl/specs/latest/1.0/
+ // REF: https://registry.khronos.org/webgl/specs/latest/1.0/
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthId);
unsigned int depthIdU = (unsigned int)depthId;
@@ -3922,6 +3941,7 @@ void rlUnloadFramebuffer(unsigned int id)
unsigned int rlLoadVertexBuffer(const void *buffer, int size, bool dynamic)
{
unsigned int id = 0;
+ if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; }
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
glGenBuffers(1, &id);
@@ -3936,6 +3956,7 @@ unsigned int rlLoadVertexBuffer(const void *buffer, int size, bool dynamic)
unsigned int rlLoadVertexBufferElement(const void *buffer, int size, bool dynamic)
{
unsigned int id = 0;
+ if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; }
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
glGenBuffers(1, &id);
@@ -4101,12 +4122,12 @@ void rlDisableStatePointer(int vertexAttribType)
unsigned int rlLoadVertexArray(void)
{
unsigned int vaoId = 0;
+ if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return vaoId; }
+
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- if (RLGL.ExtSupported.vao)
- {
- glGenVertexArrays(1, &vaoId);
- }
+ if (RLGL.ExtSupported.vao) glGenVertexArrays(1, &vaoId);
#endif
+
return vaoId;
}
@@ -4161,6 +4182,7 @@ void rlUnloadVertexBuffer(unsigned int vboId)
unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode)
{
unsigned int id = 0;
+ if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; }
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
unsigned int vertexShaderId = 0;
@@ -4303,6 +4325,7 @@ unsigned int rlCompileShader(const char *shaderCode, int type)
unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId)
{
unsigned int programId = 0;
+ if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return programId; }
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
GLint success = 0;
@@ -4426,8 +4449,6 @@ void rlSetUniform(int locIndex, const void *value, int uniformType, int count)
#endif
case RL_SHADER_UNIFORM_SAMPLER2D: glUniform1iv(locIndex, count, (int *)value); break;
default: TRACELOG(RL_LOG_WARNING, "SHADER: Failed to set uniform value, data type not recognized");
-
- // TODO: Support glUniform1uiv(), glUniform2uiv(), glUniform3uiv(), glUniform4uiv()
}
#endif
}
@@ -5203,24 +5224,36 @@ static int rlGetPixelDataSize(int width, int height, int format)
case RL_PIXELFORMAT_COMPRESSED_ETC1_RGB:
case RL_PIXELFORMAT_COMPRESSED_ETC2_RGB:
case RL_PIXELFORMAT_COMPRESSED_PVRT_RGB:
- case RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
+ case RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA: // 8 bytes per each 4x4 block
+ {
+ int blockWidth = (width + 3)/4;
+ int blockHeight = (height + 3)/4;
+ dataSize = blockWidth*blockHeight*8;
+ } break;
case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA:
case RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA:
case RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA:
- case RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break;
- case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break;
+ case RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: // 16 bytes per each 4x4 block
+ {
+ int blockWidth = (width + 3)/4;
+ int blockHeight = (height + 3)/4;
+ dataSize = blockWidth*blockHeight*16;
+ } break;
+ case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: // 4 bytes per each 4x4 block
+ {
+ int blockWidth = (width + 3)/4;
+ int blockHeight = (height + 3)/4;
+ dataSize = blockWidth*blockHeight*4;
+ } break;
default: break;
}
- double bytesPerPixel = (double)bpp/8.0;
- dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes
-
- // Most compressed formats works on 4x4 blocks,
- // if texture is smaller, minimum dataSize is 8 or 16
- if ((width < 4) && (height < 4))
+ // Compute dataSize for uncompressed texture data (no blocks)
+ if ((format >= RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) &&
+ (format <= RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16))
{
- if ((format >= RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA)) dataSize = 8;
- else if ((format >= RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) dataSize = 16;
+ double bytesPerPixel = (double)bpp/8.0;
+ dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes
}
return dataSize;
diff --git a/src/rmodels.c b/src/rmodels.c
index 3c904c396..665b94147 100644
--- a/src/rmodels.c
+++ b/src/rmodels.c
@@ -127,10 +127,13 @@
// Defines and Macros
//----------------------------------------------------------------------------------
#ifndef MAX_MATERIAL_MAPS
- #define MAX_MATERIAL_MAPS 12 // Maximum number of maps supported
+ #define MAX_MATERIAL_MAPS 12 // Maximum number of maps supported
#endif
#ifndef MAX_MESH_VERTEX_BUFFERS
- #define MAX_MESH_VERTEX_BUFFERS 9 // Maximum vertex buffers (VBO) per mesh
+ #define MAX_MESH_VERTEX_BUFFERS 9 // Maximum vertex buffers (VBO) per mesh
+#endif
+#ifndef MAX_FILEPATH_LENGTH
+ #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value)
#endif
//----------------------------------------------------------------------------------
@@ -1282,6 +1285,8 @@ void UploadMesh(Mesh *mesh, bool dynamic)
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
mesh->vaoId = rlLoadVertexArray();
+ if (mesh->vaoId == 0) return;
+
rlEnableVertexArray(mesh->vaoId);
// NOTE: Vertex attributes must be uploaded considering default locations points and available vertex data
@@ -1435,7 +1440,7 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
#define GL_COLOR_ARRAY 0x8076
#define GL_TEXTURE_COORD_ARRAY 0x8078
- rlEnableTexture(material.maps[MATERIAL_MAP_DIFFUSE].texture.id);
+ if (mesh.texcoords && material.maps[MATERIAL_MAP_DIFFUSE].texture.id > 0) rlEnableTexture(material.maps[MATERIAL_MAP_DIFFUSE].texture.id);
if (mesh.animVertices) rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animVertices);
else rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices);
@@ -1470,6 +1475,8 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
// Bind shader program
rlEnableShader(material.shader.id);
+ if (material.shader.locs == NULL) return;
+
// Send required data to shader (matrices, values)
//-----------------------------------------------------
// Upload to shader material.colDiffuse
@@ -2060,7 +2067,7 @@ bool ExportMeshAsCode(Mesh mesh, const char *fileName)
// Get file name from path and convert variable name to uppercase
char varFileName[256] = { 0 };
- strcpy(varFileName, GetFileNameWithoutExt(fileName));
+ strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1); // NOTE: Using function provided by [rcore] module
for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
// Add image information
@@ -2352,8 +2359,6 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame)
Mesh mesh = model.meshes[m];
Vector3 animVertex = { 0 };
Vector3 animNormal = { 0 };
- Matrix boneMatrix = { 0 };
- Matrix InverseBoneMatrix = { 0 };
int boneId = 0;
int boneCounter = 0;
float boneWeight = 0.0;
@@ -2361,50 +2366,47 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame)
const int vValues = mesh.vertexCount*3;
// Skip if missing bone data, causes segfault without on some models
- if ((mesh.boneWeights == NULL) || (mesh.boneIds == NULL)) continue;
+ if ((mesh.boneWeights == NULL) || (mesh.boneIds == NULL)) continue;
- // Iterates over 4 bones per vertex
- for (int j = 0; j < 4; j++, boneCounter++)
- {
- boneWeight = mesh.boneWeights[boneCounter];
- boneId = mesh.boneIds[boneCounter];
+ for (int vCounter = 0; vCounter < vValues; vCounter += 3)
+ {
+ mesh.animVertices[vCounter] = 0;
+ mesh.animVertices[vCounter + 1] = 0;
+ mesh.animVertices[vCounter + 2] = 0;
+ if (mesh.animNormals != NULL)
+ {
+ mesh.animNormals[vCounter] = 0;
+ mesh.animNormals[vCounter + 1] = 0;
+ mesh.animNormals[vCounter + 2] = 0;
+ }
- // Early stop when no transformation will be applied
- if (boneWeight == 0.0f) continue;
+ // Iterates over 4 bones per vertex
+ for (int j = 0; j < 4; j++, boneCounter++)
+ {
+ boneWeight = mesh.boneWeights[boneCounter];
+ boneId = mesh.boneIds[boneCounter];
- boneMatrix = model.meshes[m].boneMatrices[boneId];
- InverseBoneMatrix = MatrixTranspose(MatrixInvert(boneMatrix));
+ // Early stop when no transformation will be applied
+ if (boneWeight == 0.0f) continue;
+ animVertex = (Vector3){ mesh.vertices[vCounter], mesh.vertices[vCounter + 1], mesh.vertices[vCounter + 2] };
+ animVertex = Vector3Transform(animVertex,model.meshes[m].boneMatrices[boneId]);
+ mesh.animVertices[vCounter] += animVertex.x*boneWeight;
+ mesh.animVertices[vCounter+1] += animVertex.y*boneWeight;
+ mesh.animVertices[vCounter+2] += animVertex.z*boneWeight;
+ updated = true;
- for (int vCounter = 0; vCounter < vValues; vCounter += 3)
- {
- mesh.animVertices[vCounter] = 0;
- mesh.animVertices[vCounter + 1] = 0;
- mesh.animVertices[vCounter + 2] = 0;
- if (mesh.animNormals != NULL)
- {
- mesh.animNormals[vCounter] = 0;
- mesh.animNormals[vCounter + 1] = 0;
- mesh.animNormals[vCounter + 2] = 0;
- }
- animVertex = (Vector3){ mesh.vertices[vCounter], mesh.vertices[vCounter + 1], mesh.vertices[vCounter + 2] };
- animVertex = Vector3Transform(animVertex, boneMatrix);
- mesh.animVertices[vCounter] += animVertex.x*boneWeight;
- mesh.animVertices[vCounter+1] += animVertex.y*boneWeight;
- mesh.animVertices[vCounter+2] += animVertex.z*boneWeight;
- updated = true;
-
- // Normals processing
- // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
- if ((mesh.normals != NULL) && (mesh.animNormals != NULL))
- {
- animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] };
- animNormal = Vector3Transform(animNormal, InverseBoneMatrix);
- mesh.animNormals[vCounter] += animNormal.x*boneWeight;
- mesh.animNormals[vCounter + 1] += animNormal.y*boneWeight;
- mesh.animNormals[vCounter + 2] += animNormal.z*boneWeight;
- }
- }
- }
+ // Normals processing
+ // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
+ if ((mesh.normals != NULL) && (mesh.animNormals != NULL ))
+ {
+ animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] };
+ animNormal = Vector3Transform(animNormal, MatrixTranspose(MatrixInvert(model.meshes[m].boneMatrices[boneId])));
+ mesh.animNormals[vCounter] += animNormal.x*boneWeight;
+ mesh.animNormals[vCounter + 1] += animNormal.y*boneWeight;
+ mesh.animNormals[vCounter + 2] += animNormal.z*boneWeight;
+ }
+ }
+ }
if (updated)
{
@@ -3294,7 +3296,7 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
for (int z = 0; z < cubicmap.height; ++z)
{
- for (int x = 0; x < cubicmap.width; ++x)
+ for (int x = 0; x < cubicmap.width; x++)
{
// Define the 8 vertex of the cube, we will combine them accordingly later...
Vector3 v1 = { w*(x - 0.5f), h2, h*(z - 0.5f) };
@@ -3656,7 +3658,7 @@ void GenMeshTangents(Mesh *mesh)
for (int t = 0; t < mesh->triangleCount; t++)
{
// Get triangle vertex indices
- int i0, i1, i2;
+ int i0 = 0, i1 = 0, i2 = 0;
if (mesh->indices != NULL)
{
@@ -3850,7 +3852,6 @@ void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float
// Draw a model points
// WARNING: OpenGL ES 2.0 does not support point mode drawing
-// TODO: gate these properly for non es 2.0 versions only
void DrawModelPoints(Model model, Vector3 position, float scale, Color tint)
{
rlEnablePointMode();
@@ -4152,7 +4153,9 @@ RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform)
// Test against all triangles in mesh
for (int i = 0; i < triangleCount; i++)
{
- Vector3 a, b, c;
+ Vector3 a = { 0 };
+ Vector3 b = { 0 };
+ Vector3 c = { 0 };
Vector3 *vertdata = (Vector3 *)mesh.vertices;
if (mesh.indices)
@@ -4195,8 +4198,10 @@ RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3
RayCollision collision = { 0 };
Vector3 edge1 = { 0 };
Vector3 edge2 = { 0 };
- Vector3 p, q, tv;
- float det, invDet, u, v, t;
+ Vector3 p = { 0 };
+ Vector3 q = { 0 };
+ Vector3 tv = { 0 };
+ float det = 0.0f, invDet = 0.0f, u = 0.0f, v = 0.0f, t = 0.0f;
// Find vectors for two edges sharing V1
edge1 = Vector3Subtract(p2, p1);
@@ -4312,8 +4317,8 @@ static Model LoadOBJ(const char *fileName)
return model;
}
- char currentDir[1024] = { 0 };
- strcpy(currentDir, GetWorkingDirectory()); // Save current working directory
+ char currentDir[MAX_FILEPATH_LENGTH] = { 0 };
+ strncpy(currentDir, GetWorkingDirectory(), MAX_FILEPATH_LENGTH - 1); // Save current working directory
const char *workingDir = GetDirectoryPath(fileName); // Switch to OBJ directory for material path correctness
if (CHDIR(workingDir) != 0) TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir);
@@ -4434,10 +4439,12 @@ static Model LoadOBJ(const char *fileName)
model.meshes[i].vertices = (float *)MemAlloc(sizeof(float)*vertexCount*3);
model.meshes[i].normals = (float *)MemAlloc(sizeof(float)*vertexCount*3);
- model.meshes[i].texcoords = (float *)MemAlloc(sizeof(float)*vertexCount*2);
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ model.meshes[i].texcoords = (float *)MemAlloc(sizeof(float)*vertexCount*2);
model.meshes[i].colors = (unsigned char *)MemAlloc(sizeof(unsigned char)*vertexCount*4);
#else
+ if (objAttributes.texcoords != NULL && objAttributes.num_texcoords > 0) model.meshes[i].texcoords = (float *)MemAlloc(sizeof(float)*vertexCount*2);
+ else model.meshes[i].texcoords = NULL;
model.meshes[i].colors = NULL;
#endif
}
@@ -4493,16 +4500,11 @@ static Model LoadOBJ(const char *fileName)
for (int i = 0; i < 3; i++) model.meshes[meshIndex].vertices[localMeshVertexCount*3 + i] = objAttributes.vertices[vertIndex*3 + i];
- if ((objAttributes.texcoords != NULL) && (texcordIndex != TINYOBJ_INVALID_INDEX) && (texcordIndex >= 0))
+ if ((objAttributes.texcoords != NULL) && (texcordIndex != TINYOBJ_INVALID_INDEX) && (texcordIndex >= 0) && (model.meshes[meshIndex].texcoords))
{
for (int i = 0; i < 2; i++) model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + i] = objAttributes.texcoords[texcordIndex*2 + i];
model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1] = 1.0f - model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1];
}
- else
- {
- model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 0] = 0.0f;
- model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1] = 0.0f;
- }
if ((objAttributes.normals != NULL) && (normalIndex != TINYOBJ_INVALID_INDEX) && (normalIndex >= 0))
{
@@ -5027,17 +5029,15 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou
animations[a].boneCount = iqmHeader->num_poses;
animations[a].bones = (BoneInfo *)RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo));
animations[a].framePoses = (Transform **)RL_MALLOC(anim[a].num_frames*sizeof(Transform *));
- memcpy(animations[a].name, fileDataPtr + iqmHeader->ofs_text + anim[a].name, 32); // I don't like this 32 here
+ memcpy(animations[a].name, fileDataPtr + iqmHeader->ofs_text + anim[a].name, 32);
TRACELOG(LOG_INFO, "IQM Anim %s", animations[a].name);
- // animations[a].framerate = anim.framerate; // TODO: Use animation framerate data?
+ //animations[a].framerate = anim.framerate; // TODO: Use animation framerate data?
for (unsigned int j = 0; j < iqmHeader->num_poses; j++)
{
// If animations and skeleton are in the same file, copy bone names to anim
- if (iqmHeader->num_joints > 0)
- memcpy(animations[a].bones[j].name, fileDataPtr + iqmHeader->ofs_text + joints[j].name, BONE_NAME_LENGTH*sizeof(char));
- else
- strcpy(animations[a].bones[j].name, "ANIMJOINTNAME"); // default bone name otherwise
+ if (iqmHeader->num_joints > 0) memcpy(animations[a].bones[j].name, fileDataPtr + iqmHeader->ofs_text + joints[j].name, BONE_NAME_LENGTH*sizeof(char));
+ else memcpy(animations[a].bones[j].name, "ANIMJOINTNAME", 13); // Default bone name otherwise
animations[a].bones[j].parent = poses[j].parent;
}
@@ -5587,6 +5587,56 @@ static Model LoadGLTF(const char *fileName)
vertices[3*k+2] = vt.z;
}
}
+ else if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_16u))
+ {
+ // Init raylib mesh vertices to copy glTF attribute data
+ model.meshes[meshIndex].vertexCount = (int)attribute->count;
+ model.meshes[meshIndex].vertices = (float *)RL_MALLOC(attribute->count*3*sizeof(float));
+
+ // Load data into a temp buffer to be converted to raylib data type
+ unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*3*sizeof(unsigned short));
+ LOAD_ATTRIBUTE(attribute, 3, unsigned short, temp);
+
+ // Convert data to raylib vertex data type (float) the matrix will scale it to the correct size as a float
+ for (unsigned int t = 0; t < attribute->count*3; t++) model.meshes[meshIndex].vertices[t] = (float)temp[t];
+
+ RL_FREE(temp);
+
+ // Transform the vertices
+ float *vertices = model.meshes[meshIndex].vertices;
+ for (unsigned int k = 0; k < attribute->count; k++)
+ {
+ Vector3 vt = Vector3Transform((Vector3){ vertices[3*k], vertices[3*k + 1], vertices[3*k + 2] }, worldMatrix);
+ vertices[3*k] = vt.x;
+ vertices[3*k + 1] = vt.y;
+ vertices[3*k + 2] = vt.z;
+ }
+ }
+ else if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_16))
+ {
+ // Init raylib mesh vertices to copy glTF attribute data
+ model.meshes[meshIndex].vertexCount = (int)attribute->count;
+ model.meshes[meshIndex].vertices = (float *)RL_MALLOC(attribute->count*3*sizeof(float));
+
+ // Load data into a temp buffer to be converted to raylib data type
+ short *temp = (short *)RL_MALLOC(attribute->count*3*sizeof(short));
+ LOAD_ATTRIBUTE(attribute, 3, short, temp);
+
+ // Convert data to raylib vertex data type (float) the matrix will scale it to the correct size as a float
+ for (unsigned int t = 0; t < attribute->count*3; t++) model.meshes[meshIndex].vertices[t] = (float)temp[t];
+
+ RL_FREE(temp);
+
+ // Transform the vertices
+ float *vertices = model.meshes[meshIndex].vertices;
+ for (unsigned int k = 0; k < attribute->count; k++)
+ {
+ Vector3 vt = Vector3Transform((Vector3){ vertices[3*k], vertices[3*k + 1], vertices[3*k + 2] }, worldMatrix);
+ vertices[3*k] = vt.x;
+ vertices[3*k + 1] = vt.y;
+ vertices[3*k + 2] = vt.z;
+ }
+ }
else TRACELOG(LOG_WARNING, "MODEL: [%s] Vertices attribute data format not supported, use vec3 float", fileName);
}
}
@@ -5615,6 +5665,78 @@ static Model LoadGLTF(const char *fileName)
normals[3*k+2] = nt.z;
}
}
+ else if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_16))
+ {
+ // Init raylib mesh normals to copy glTF attribute data
+ model.meshes[meshIndex].normals = (float *)RL_MALLOC(attribute->count*3*sizeof(float));
+
+ // Load data into a temp buffer to be converted to raylib data type
+ short *temp = (short *)RL_MALLOC(attribute->count*3*sizeof(short));
+ LOAD_ATTRIBUTE(attribute, 3, short, temp);
+
+ // Convert data to raylib normal data type (float)
+ for (unsigned int t = 0; t < attribute->count*3; t++) model.meshes[meshIndex].normals[t] = (float)temp[t];
+
+ RL_FREE(temp);
+
+ // Transform the normals
+ float *normals = model.meshes[meshIndex].normals;
+ for (unsigned int k = 0; k < attribute->count; k++)
+ {
+ Vector3 nt = Vector3Normalize(Vector3Transform((Vector3){ normals[3*k], normals[3*k + 1], normals[3*k + 2] }, worldMatrixNormals));
+ normals[3*k] = nt.x;
+ normals[3*k + 1] = nt.y;
+ normals[3*k + 2] = nt.z;
+ }
+ }
+ else if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_8u))
+ {
+ // Init raylib mesh normals to copy glTF attribute data
+ model.meshes[meshIndex].normals = (float *)RL_MALLOC(attribute->count*3*sizeof(float));
+
+ // Load data into a temp buffer to be converted to raylib data type
+ unsigned char *temp = (unsigned char *)RL_MALLOC(attribute->count*3*sizeof(unsigned char));
+ LOAD_ATTRIBUTE(attribute, 3, unsigned char, temp);
+
+ // Convert data to raylib normal data type (float)
+ for (unsigned int t = 0; t < attribute->count*3; t++) model.meshes[meshIndex].normals[t] = (float)temp[t];
+
+ RL_FREE(temp);
+
+ // Transform the normals
+ float *normals = model.meshes[meshIndex].normals;
+ for (unsigned int k = 0; k < attribute->count; k++)
+ {
+ Vector3 nt = Vector3Normalize(Vector3Transform((Vector3){ normals[3*k], normals[3*k + 1], normals[3*k + 2] }, worldMatrixNormals));
+ normals[3*k] = nt.x;
+ normals[3*k + 1] = nt.y;
+ normals[3*k + 2] = nt.z;
+ }
+ }
+ else if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_8))
+ {
+ // Init raylib mesh normals to copy glTF attribute data
+ model.meshes[meshIndex].normals = (float *)RL_MALLOC(attribute->count*3*sizeof(float));
+
+ // Load data into a temp buffer to be converted to raylib data type
+ char *temp = (char *)RL_MALLOC(attribute->count*3*sizeof(char));
+ LOAD_ATTRIBUTE(attribute, 3, char, temp);
+
+ // Convert data to raylib normal data type (float)
+ for (unsigned int t = 0; t < attribute->count*3; t++) model.meshes[meshIndex].normals[t] = (float)temp[t];
+
+ RL_FREE(temp);
+
+ // Transform the normals
+ float *normals = model.meshes[meshIndex].normals;
+ for (unsigned int k = 0; k < attribute->count; k++)
+ {
+ Vector3 nt = Vector3Normalize(Vector3Transform((Vector3){ normals[3*k], normals[3*k + 1], normals[3*k + 2] }, worldMatrixNormals));
+ normals[3*k] = nt.x;
+ normals[3*k + 1] = nt.y;
+ normals[3*k + 2] = nt.z;
+ }
+ }
else TRACELOG(LOG_WARNING, "MODEL: [%s] Normals attribute data format not supported, use vec3 float", fileName);
}
}
@@ -6231,7 +6353,7 @@ static bool GetPoseAtTimeGLTF(cgltf_interpolation_type interpolationType, cgltf_
return true;
}
-#define GLTF_ANIMDELAY 17 // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms)
+#define GLTF_FRAMERATE 60.0f // glTF animation framerate (frames per second)
static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCount)
{
@@ -6351,13 +6473,13 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo
if (animData.name != NULL) strncpy(animations[i].name, animData.name, sizeof(animations[i].name) - 1);
- animations[i].frameCount = (int)(animDuration*1000.0f/GLTF_ANIMDELAY) + 1;
+ animations[i].frameCount = (int)(animDuration*GLTF_FRAMERATE) + 1;
animations[i].framePoses = (Transform **)RL_MALLOC(animations[i].frameCount*sizeof(Transform *));
for (int j = 0; j < animations[i].frameCount; j++)
{
animations[i].framePoses[j] = (Transform *)RL_MALLOC(animations[i].boneCount*sizeof(Transform));
- float time = ((float) j*GLTF_ANIMDELAY)/1000.0f;
+ float time = (float)j / GLTF_FRAMERATE;
for (int k = 0; k < animations[i].boneCount; k++)
{
@@ -6857,7 +6979,7 @@ static Model LoadM3D(const char *fileName)
// Add a special "no bone" bone
model.bones[i].parent = -1;
- strcpy(model.bones[i].name, "NO BONE");
+ memcpy(model.bones[i].name, "NO BONE", 7);
model.bindPose[i].translation.x = 0.0f;
model.bindPose[i].translation.y = 0.0f;
model.bindPose[i].translation.z = 0.0f;
@@ -6949,7 +7071,7 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou
// A special, never transformed "no bone" bone, used for boneless vertices
animations[a].bones[i].parent = -1;
- strcpy(animations[a].bones[i].name, "NO BONE");
+ memcpy(animations[a].bones[i].name, "NO BONE", 7);
// M3D stores frames at arbitrary intervals with sparse skeletons. We need full skeletons at
// regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones
diff --git a/src/rshapes.c b/src/rshapes.c
index 2b5854f86..528a362d5 100644
--- a/src/rshapes.c
+++ b/src/rshapes.c
@@ -1169,9 +1169,9 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co
}
// Draw rectangle with rounded edges
-// TODO: This function should be refactored to use RL_LINES, for consistency with other Draw*Lines()
void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, Color color)
{
+ // NOTE: For line thicknes <=1.0f we use RL_LINES, otherwise wee use RL_QUADS/RL_TRIANGLES
DrawRectangleRoundedLinesEx(rec, roundness, segments, 1.0f, color);
}
@@ -1395,7 +1395,6 @@ void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, f
{
// Use LINES to draw the outline
rlBegin(RL_LINES);
-
// Draw all the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner
for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop
{
@@ -1418,7 +1417,6 @@ void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, f
rlVertex2f(point[i].x, point[i].y);
rlVertex2f(point[i + 1].x, point[i + 1].y);
}
-
rlEnd();
}
}
diff --git a/src/rtext.c b/src/rtext.c
index b4cd560ba..453ed4507 100644
--- a/src/rtext.c
+++ b/src/rtext.c
@@ -67,7 +67,7 @@
#include // Required for: malloc(), free()
#include // Required for: vsprintf()
-#include // Required for: strcmp(), strstr(), strcpy(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()]
+#include // Required for: strcmp(), strstr(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()]
#include // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()]
#include // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()]
@@ -127,13 +127,12 @@
//----------------------------------------------------------------------------------
// Global variables
//----------------------------------------------------------------------------------
-extern bool isGpuReady;
#if defined(SUPPORT_DEFAULT_FONT)
// Default font provided by raylib
// NOTE: Default font is loaded on InitWindow() and disposed on CloseWindow() [module: core]
static Font defaultFont = { 0 };
#endif
-static int textLineSpacing = 2; // Text vertical line spacing in pixels (between lines)
+static int textLineSpacing = 2; // Text vertical line spacing in pixels (between lines)
//----------------------------------------------------------------------------------
// Other Modules Functions Declaration (required by text)
@@ -147,7 +146,7 @@ static int textLineSpacing = 2; // Text vertical line spacing in
static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file)
#endif
#if defined(SUPPORT_FILEFORMAT_BDF)
-static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, int *codepoints, int codepointCount, int *outFontSize);
+static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, const int *codepoints, int codepointCount, int *outFontSize);
#endif
#if defined(SUPPORT_DEFAULT_FONT)
@@ -164,12 +163,11 @@ extern void LoadFontDefault(void)
{
#define BIT_CHECK(a,b) ((a) & (1u << (b)))
- // check to see if we have allready allocated the font for an image, and if we don't need to upload, then just return
- if (defaultFont.glyphs != NULL && !isGpuReady)
- return;
+ // Check to see if we have already allocated the font for an image, and if we don't need to upload, then just return
+ if (defaultFont.glyphs != NULL) return;
// NOTE: Using UTF-8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement
- // Ref: http://www.utf8-chartable.de/unicode-utf8-table.pl
+ // REF: http://www.utf8-chartable.de/unicode-utf8-table.pl
defaultFont.glyphCount = 224; // Number of glyphs included in our default font
defaultFont.glyphPadding = 0; // Characters padding
@@ -264,17 +262,14 @@ extern void LoadFontDefault(void)
counter++;
}
- if (isGpuReady)
- {
- defaultFont.texture = LoadTextureFromImage(imFont);
+ defaultFont.texture = LoadTextureFromImage(imFont);
- // we have already loaded the font glyph data an image, and the GPU is ready, we are done
- // if we don't do this, we will leak memory by reallocating the glyphs and rects
- if (defaultFont.glyphs != NULL)
- {
- UnloadImage(imFont);
- return;
- }
+ // we have already loaded the font glyph data an image, and the GPU is ready, we are done
+ // if we don't do this, we will leak memory by reallocating the glyphs and rects
+ if (defaultFont.glyphs != NULL)
+ {
+ UnloadImage(imFont);
+ return;
}
// Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, glyphCount
@@ -331,7 +326,7 @@ extern void LoadFontDefault(void)
extern void UnloadFontDefault(void)
{
for (int i = 0; i < defaultFont.glyphCount; i++) UnloadImage(defaultFont.glyphs[i].image);
- if (isGpuReady) UnloadTexture(defaultFont.texture);
+ UnloadTexture(defaultFont.texture);
RL_FREE(defaultFont.glyphs);
RL_FREE(defaultFont.recs);
defaultFont.glyphCount = 0;
@@ -385,17 +380,15 @@ Font LoadFont(const char *fileName)
{
Image image = LoadImage(fileName);
if (image.data != NULL) font = LoadFontFromImage(image, MAGENTA, FONT_TTF_DEFAULT_FIRST_CHAR);
+ else font = GetFontDefault();
UnloadImage(image);
}
- if (isGpuReady)
+ if (font.texture.id == 0) TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load font texture -> Using default font", fileName);
+ else
{
- if (font.texture.id == 0) TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load font texture -> Using default font", fileName);
- else
- {
- SetTextureFilter(font.texture, TEXTURE_FILTER_POINT); // By default, we set point filter (the best performance)
- TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", font.baseSize, font.glyphCount);
- }
+ SetTextureFilter(font.texture, TEXTURE_FILTER_POINT); // By default, we set point filter (the best performance)
+ TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", font.baseSize, font.glyphCount);
}
return font;
@@ -516,7 +509,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
};
// Set font with all data parsed from image
- if (isGpuReady) font.texture = LoadTextureFromImage(fontClear); // Convert processed image to OpenGL texture
+ font.texture = LoadTextureFromImage(fontClear); // Convert processed image to OpenGL texture
font.glyphCount = index;
font.glyphPadding = 0;
@@ -585,7 +578,7 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int
font.glyphPadding = FONT_TTF_DEFAULT_CHARS_PADDING;
Image atlas = GenImageFontAtlas(font.glyphs, &font.recs, font.glyphCount, font.baseSize, font.glyphPadding, 0);
- if (isGpuReady) font.texture = LoadTextureFromImage(atlas);
+ font.texture = LoadTextureFromImage(atlas);
// Update glyphs[i].image to use alpha, required to be used on ImageDrawText()
for (int i = 0; i < font.glyphCount; i++)
@@ -647,7 +640,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
{
bool genFontChars = false;
stbtt_fontinfo fontInfo = { 0 };
- int *requiredCodepoints = (int *)codepoints;
+ int *requiredCodepoints = (int *)codepoints; // TODO: Should we create a shallow copy to avoid "dealing" with a const user array?
if (stbtt_InitFont(&fontInfo, (unsigned char *)fileData, 0)) // Initialize font for data reading
{
@@ -656,7 +649,9 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
// Calculate font basic metrics
// NOTE: ascent is equivalent to font baseline
- int ascent, descent, lineGap;
+ int ascent = 0;
+ int descent = 0;
+ int lineGap = 0;
stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
// In case no chars count provided, default to 95
@@ -705,7 +700,11 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
switch (type)
{
case FONT_DEFAULT:
- case FONT_BITMAP: glyphs[k].image.data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, cp, &cpWidth, &cpHeight, &glyphs[k].offsetX, &glyphs[k].offsetY); break;
+ case FONT_BITMAP:
+ {
+ glyphs[k].image.data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, cp,
+ &cpWidth, &cpHeight, &glyphs[k].offsetX, &glyphs[k].offsetY);
+ } break;
case FONT_SDF:
{
if (cp != 32)
@@ -724,7 +723,8 @@ 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);
- if (cpHeight > fontSize) TRACELOG(LOG_WARNING, "FONT: [0x%04x] Glyph height is bigger than requested font size: %i > %i", cp, cpHeight, (int)fontSize);
+ // WARNING: If requested SDF font, sdf-glyph height is definitely bigger than fontSize due to FONT_SDF_CHAR_PADDING
+ if ((type != FONT_SDF) && (cpHeight > fontSize)) TRACELOG(LOG_WARNING, "FONT: [0x%04x] Glyph height is bigger than requested font size: %i > %i", cp, cpHeight, (int)fontSize);
// Load glyph image
glyphs[k].image.width = cpWidth;
@@ -1008,7 +1008,7 @@ void UnloadFont(Font font)
if (font.texture.id != GetFontDefault().texture.id)
{
UnloadFontData(font.glyphs, font.glyphCount);
- if (isGpuReady) UnloadTexture(font.texture);
+ UnloadTexture(font.texture);
RL_FREE(font.recs);
TRACELOGD("FONT: Unloaded font data from RAM and VRAM");
@@ -1339,8 +1339,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
{
Vector2 textSize = { 0 };
- if ((isGpuReady && (font.texture.id == 0)) ||
- (text == NULL) || (text[0] == '\0')) return textSize; // Security check
+ if ((font.texture.id == 0) || (text == NULL) || (text[0] == '\0')) return textSize; // Security check
int size = TextLength(text); // Get size in bytes of text
int tempByteCounter = 0; // Used to count longer text line num chars
@@ -1451,26 +1450,32 @@ Rectangle GetGlyphAtlasRec(Font font, int codepoint)
// NOTE: Returned lines end with null terminator '\0'
char **LoadTextLines(const char *text, int *count)
{
- int lineCount = 1;
- int textSize = (int)strlen(text);
+ char **lines = NULL;
+ int lineCount = 0;
- // Text pass to get required line count
- for (int i = 0; i < textSize; i++)
+ if (text != NULL)
{
- if (text[i] == '\n') lineCount++;
- }
+ int textLength = TextLength(text);
+ lineCount = 1;
- char **lines = (char **)RL_CALLOC(lineCount, sizeof(char *));
- for (int i = 0, l = 0, lineLen = 0; i <= textSize; i++)
- {
- if ((text[i] == '\n') || (text[i] == '\0'))
+ // First text scan pass to get required line count
+ for (int i = 0; i < textLength; i++)
{
- lines[l] = (char *)RL_CALLOC(lineLen + 1, 1);
- strncpy(lines[l], &text[i - lineLen], lineLen);
- lineLen = 0;
- l++;
+ if (text[i] == '\n') lineCount++;
+ }
+
+ lines = (char **)RL_CALLOC(lineCount, sizeof(char *));
+ for (int i = 0, l = 0, lineLen = 0; i <= textLength; i++)
+ {
+ if ((text[i] == '\n') || (text[i] == '\0'))
+ {
+ lines[l] = (char *)RL_CALLOC(lineLen + 1, 1);
+ strncpy(lines[l], &text[i - lineLen], lineLen);
+ lineLen = 0;
+ l++;
+ }
+ else lineLen++;
}
- else lineLen++;
}
*count = lineCount;
@@ -1512,23 +1517,26 @@ const char *TextFormat(const char *text, ...)
static int index = 0;
char *currentBuffer = buffers[index];
- memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using
-
- va_list args;
- va_start(args, text);
- int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
- va_end(args);
-
- // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured
- if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
+ memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using
+
+ if (text != NULL)
{
- // Inserting "..." at the end of the string to mark as truncated
- char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
- sprintf(truncBuffer, "...");
- }
+ va_list args;
+ va_start(args, text);
+ int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
+ va_end(args);
- index += 1; // Move to next buffer for next function call
- if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
+ // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occurred
+ if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
+ {
+ // Inserting "..." at the end of the string to mark as truncated
+ char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
+ snprintf(truncBuffer, 4, "...");
+ }
+
+ index += 1; // Move to next buffer for next function call
+ if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
+ }
return currentBuffer;
}
@@ -1540,13 +1548,16 @@ int TextToInteger(const char *text)
int value = 0;
int sign = 1;
- if ((text[0] == '+') || (text[0] == '-'))
+ if (text != NULL)
{
- if (text[0] == '-') sign = -1;
- text++;
- }
+ if ((text[0] == '+') || (text[0] == '-'))
+ {
+ if (text[0] == '-') sign = -1;
+ text++;
+ }
- for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0');
+ for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0');
+ }
return value*sign;
}
@@ -1559,22 +1570,25 @@ float TextToFloat(const char *text)
float value = 0.0f;
float sign = 1.0f;
- if ((text[0] == '+') || (text[0] == '-'))
+ if (text != NULL)
{
- if (text[0] == '-') sign = -1.0f;
- text++;
- }
-
- int i = 0;
- for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
-
- if (text[i++] == '.')
- {
- float divisor = 10.0f;
- for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
+ if ((text[0] == '+') || (text[0] == '-'))
{
- value += ((float)(text[i] - '0'))/divisor;
- divisor = divisor*10.0f;
+ if (text[0] == '-') sign = -1.0f;
+ text++;
+ }
+
+ int i = 0;
+ for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
+
+ if (text[i++] == '.')
+ {
+ float divisor = 10.0f;
+ for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
+ {
+ value += ((float)(text[i] - '0'))/divisor;
+ divisor = divisor*10.0f;
+ }
}
}
@@ -1583,14 +1597,13 @@ float TextToFloat(const char *text)
#if defined(SUPPORT_TEXT_MANIPULATION)
// Copy one string to another, returns bytes copied
+// NOTE: Alternative implementation to strcpy(dst, src) from C standard library
int TextCopy(char *dst, const char *src)
{
int bytes = 0;
if ((src != NULL) && (dst != NULL))
{
- // NOTE: Alternative: use strcpy(dst, src)
-
while (*src != '\0')
{
*dst = *src;
@@ -1626,26 +1639,23 @@ const char *TextSubtext(const char *text, int position, int length)
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
- int textLength = TextLength(text);
-
- if (position >= textLength)
+ if (text != NULL)
{
- return buffer; //First char is already '\0' by memset
+ int textLength = TextLength(text);
+
+ if (position >= textLength) return buffer; // First char is already '\0' by memset
+
+ int maxLength = textLength - position;
+ if (length > maxLength) length = maxLength;
+ if (length >= MAX_TEXT_BUFFER_LENGTH) length = MAX_TEXT_BUFFER_LENGTH - 1;
+
+ // NOTE: Alternative: memcpy(buffer, text + position, length)
+
+ for (int c = 0; c < length; c++) buffer[c] = text[position + c];
+
+ buffer[length] = '\0';
}
- int maxLength = textLength - position;
- if (length > maxLength) length = maxLength;
- if (length >= MAX_TEXT_BUFFER_LENGTH) length = MAX_TEXT_BUFFER_LENGTH - 1;
-
- // NOTE: Alternative: memcpy(buffer, text + position, length)
-
- for (int c = 0 ; c < length ; c++)
- {
- buffer[c] = text[position + c];
- }
-
- buffer[length] = '\0';
-
return buffer;
}
@@ -1679,7 +1689,7 @@ char *GetTextBetween(const char *text, const char *begin, const char *end)
if (beginIndex > -1)
{
- int beginLen = (int)strlen(begin);
+ int beginLen = TextLength(begin);
int endIndex = TextFindIndex(text + beginIndex + beginLen, end);
if (endIndex > -1)
@@ -1695,84 +1705,92 @@ char *GetTextBetween(const char *text, const char *begin, const char *end)
}
// Replace text string
-// REQUIRES: strstr(), strncpy(), strcpy()
-// TODO: If (replacement == NULL) remove "search" text
+// REQUIRES: strstr(), strncpy()
+// TODO: If (replacement == "") remove "search" text
// WARNING: Allocated memory must be manually freed
char *TextReplace(const char *text, const char *search, const char *replacement)
{
char *result = NULL;
- if (!text || !search) return NULL; // Sanity check
-
- char *insertPoint = NULL; // Next insert point
- char *temp = NULL; // Temp pointer
- 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
-
- searchLen = TextLength(search);
- if (searchLen == 0) return NULL; // Empty search causes infinite loop during count
-
- replaceLen = TextLength(replacement);
-
- // Count the number of replacements needed
- insertPoint = (char *)text;
- 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);
-
- if (!result) return NULL; // Memory could not be allocated
-
- // First time through the loop, all the variable are set correctly from here on,
- // - 'temp' points to the end of the result string
- // - 'insertPoint' points to the next occurrence of replace in text
- // - 'text' points to the remainder of text after "end of replace"
- while (count--)
+ if ((text != NULL) && (search != NULL))
{
- insertPoint = (char *)strstr(text, search);
- lastReplacePos = (int)(insertPoint - text);
- temp = strncpy(temp, text, lastReplacePos) + lastReplacePos;
- temp = strcpy(temp, replacement) + replaceLen;
- text += lastReplacePos + searchLen; // Move to next "end of replace"
- }
+ 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
- // Copy remaind text part after replacement to result (pointed by moving temp)
- strcpy(temp, text);
+ textLen = TextLength(text);
+ searchLen = TextLength(search);
+ if (searchLen == 0) return NULL; // Empty search causes infinite loop during count
+
+ replaceLen = TextLength(replacement);
+
+ // Count the number of replacements needed
+ insertPoint = (char *)text;
+ for (count = 0; (temp = strstr(insertPoint, search)); count++) insertPoint = temp + searchLen;
+
+ // Allocate returning string and point temp to it
+ int tempLen = textLen + (replaceLen - searchLen)*count + 1;
+ temp = result = (char *)RL_MALLOC(tempLen);
+
+ if (!result) return NULL; // Memory could not be allocated
+
+ // First time through the loop, all the variable are set correctly from here on,
+ // - 'temp' points to the end of the result string
+ // - 'insertPoint' points to the next occurrence of replace in text
+ // - 'text' points to the remainder of text after "end of replace"
+ while (count--)
+ {
+ insertPoint = (char *)strstr(text, search);
+ lastReplacePos = (int)(insertPoint - text);
+ temp = strncpy(temp, text, tempLen - 1) + lastReplacePos;
+ tempLen -= lastReplacePos;
+ temp = strncpy(temp, replacement, tempLen - 1) + replaceLen;
+ tempLen -= replaceLen;
+
+ text += lastReplacePos + searchLen; // Move to next "end of replace"
+ }
+
+ // Copy remaind text part after replacement to result (pointed by moving temp)
+ strncpy(temp, text, tempLen - 1);
+ }
return result;
}
// Replace text between two specific strings
-// REQUIRES: strlen(), strncpy()
+// REQUIRES: strncpy()
// NOTE: If (replacement == NULL) remove "begin"[ ]"end" text
// WARNING: Returned string must be freed by user
char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement)
{
char *result = NULL;
- if (!text || !begin || !end) return NULL; // Sanity check
-
- int beginIndex = TextFindIndex(text, begin);
-
- if (beginIndex > -1)
+ if ((text != NULL) && (begin != NULL) && (end != NULL))
{
- int beginLen = (int)strlen(begin);
- int endIndex = TextFindIndex(text + beginIndex + beginLen, end);
+ int beginIndex = TextFindIndex(text, begin);
- if (endIndex > -1)
+ if (beginIndex > -1)
{
- endIndex += (beginIndex + beginLen);
+ int beginLen = TextLength(begin);
+ int endIndex = TextFindIndex(text + beginIndex + beginLen, end);
- int textLen = (int)strlen(text);
- int replaceLen = (replacement == NULL)? 0 : (int)strlen(replacement);
- int toreplaceLen = endIndex - beginIndex - beginLen;
- result = (char *)RL_CALLOC(textLen + replaceLen - toreplaceLen + 1, sizeof(char));
+ if (endIndex > -1)
+ {
+ endIndex += (beginIndex + beginLen);
- strncpy(result, text, beginIndex + beginLen); // Copy first text part
- if (replacement != NULL) strncpy(result + beginIndex + beginLen, replacement, replaceLen); // Copy replacement (if provided)
- strncpy(result + beginIndex + beginLen + replaceLen, text + endIndex, textLen - endIndex); // Copy end text part
+ int textLen = TextLength(text);
+ int replaceLen = (replacement == NULL)? 0 : TextLength(replacement);
+ int toreplaceLen = endIndex - beginIndex - beginLen;
+ result = (char *)RL_CALLOC(textLen + replaceLen - toreplaceLen + 1, sizeof(char));
+
+ strncpy(result, text, beginIndex + beginLen); // Copy first text part
+ if (replacement != NULL) strncpy(result + beginIndex + beginLen, replacement, replaceLen); // Copy replacement (if provided)
+ strncpy(result + beginIndex + beginLen + replaceLen, text + endIndex, textLen - endIndex); // Copy end text part
+ }
}
}
@@ -1783,16 +1801,21 @@ char *TextReplaceBetween(const char *text, const char *begin, const char *end, c
// WARNING: Allocated memory must be manually freed
char *TextInsert(const char *text, const char *insert, int position)
{
- int textLen = TextLength(text);
- int insertLen = TextLength(insert);
+ char *result = NULL;
- char *result = (char *)RL_MALLOC(textLen + insertLen + 1);
+ if ((text != NULL) && (insert != NULL))
+ {
+ int textLen = TextLength(text);
+ int insertLen = TextLength(insert);
- for (int i = 0; i < position; i++) result[i] = text[i];
- for (int i = position; i < insertLen + position; i++) result[i] = insert[i];
- for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i];
+ result = (char *)RL_MALLOC(textLen + insertLen + 1);
- result[textLen + insertLen] = '\0'; // Make sure text string is valid!
+ for (int i = 0; i < position; i++) result[i] = text[i];
+ for (int i = position; i < insertLen + position; i++) result[i] = insert[i];
+ for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i];
+
+ result[textLen + insertLen] = '\0'; // Add EOL
+ }
return result;
}
@@ -1874,11 +1897,13 @@ char **TextSplit(const char *text, char delimiter, int *count)
// Append text at specific position and move cursor
// WARNING: It's up to the user to make sure appended text does not overflow the buffer!
-// REQUIRES: strcpy()
void TextAppend(char *text, const char *append, int *position)
{
- strcpy(text + *position, append);
- *position += TextLength(append);
+ if ((text != NULL) && (append != NULL))
+ {
+ TextCopy(text + *position, append);
+ *position += TextLength(append);
+ }
}
// Find first text occurrence within a string
@@ -1887,9 +1912,12 @@ int TextFindIndex(const char *text, const char *search)
{
int position = -1;
- char *ptr = (char *)strstr(text, search);
+ if (text != NULL)
+ {
+ char *ptr = (char *)strstr(text, search);
- if (ptr != NULL) position = (int)(ptr - text);
+ if (ptr != NULL) position = (int)(ptr - text);
+ }
return position;
}
@@ -2023,24 +2051,29 @@ char *TextToCamel(const char *text)
// WARNING: Allocated memory must be manually freed
char *LoadUTF8(const int *codepoints, int length)
{
- // We allocate enough memory to fit all possible codepoints
- // NOTE: 5 bytes for every codepoint should be enough
- char *text = (char *)RL_CALLOC(length*5, 1);
- const char *utf8 = NULL;
- int size = 0;
-
- for (int i = 0, bytes = 0; i < length; i++)
+ char *text = NULL;
+
+ if ((codepoints != NULL) && (length > 0))
{
- utf8 = CodepointToUTF8(codepoints[i], &bytes);
- memcpy(text + size, utf8, bytes);
- size += bytes;
- }
+ // We allocate enough memory to fit all possible codepoints
+ // NOTE: 5 bytes for every codepoint should be enough
+ text = (char *)RL_CALLOC(length*5, 1);
+ const char *utf8 = NULL;
+ int size = 0;
- // Create second buffer and copy data manually to it
- char *temp = (char *)RL_CALLOC(size + 1, 1);
- memcpy(temp, text, size);
- RL_FREE(text);
- text = temp;
+ for (int i = 0, bytes = 0; i < length; i++)
+ {
+ utf8 = CodepointToUTF8(codepoints[i], &bytes);
+ memcpy(text + size, utf8, bytes);
+ size += bytes;
+ }
+
+ // Create second buffer and copy data manually to it
+ char *temp = (char *)RL_CALLOC(size + 1, 1);
+ memcpy(temp, text, size);
+ RL_FREE(text);
+ text = temp;
+ }
return text;
}
@@ -2054,28 +2087,31 @@ void UnloadUTF8(char *text)
// Load all codepoints from a UTF-8 text string, codepoints count returned by parameter
int *LoadCodepoints(const char *text, int *count)
{
- int textLength = TextLength(text);
-
- int codepointSize = 0;
+ int *codepoints = NULL;
int codepointCount = 0;
-
- // Allocate a big enough buffer to store as many codepoints as text bytes
- int *codepoints = (int *)RL_CALLOC(textLength, sizeof(int));
-
- for (int i = 0; i < textLength; codepointCount++)
+
+ if (text != NULL)
{
- codepoints[codepointCount] = GetCodepointNext(text + i, &codepointSize);
- i += codepointSize;
+ int textLength = TextLength(text);
+
+ // Allocate a big enough buffer to store as many codepoints as text bytes
+ codepoints = (int *)RL_CALLOC(textLength, sizeof(int));
+
+ int codepointSize = 0;
+ for (int i = 0; i < textLength; codepointCount++)
+ {
+ codepoints[codepointCount] = GetCodepointNext(text + i, &codepointSize);
+ i += codepointSize;
+ }
+
+ // Create second buffer and copy data manually to it
+ int *temp = (int *)RL_CALLOC(codepointCount, sizeof(int));
+ for (int i = 0; i < codepointCount; i++) temp[i] = codepoints[i];
+ RL_FREE(codepoints);
+ codepoints = temp;
}
- // Create second buffer and copy data manually to it
- int *temp = (int *)RL_CALLOC(codepointCount, sizeof(int));
- for (int i = 0; i < codepointCount; i++) temp[i] = codepoints[i];
- RL_FREE(codepoints);
- codepoints = temp;
-
*count = codepointCount;
-
return codepoints;
}
@@ -2092,14 +2128,15 @@ int GetCodepointCount(const char *text)
unsigned int length = 0;
const char *ptr = text;
- while (*ptr != '\0')
+ if (ptr != NULL)
{
- int next = 0;
- GetCodepointNext(ptr, &next);
-
- ptr += next;
-
- length++;
+ while (*ptr != '\0')
+ {
+ int next = 0;
+ GetCodepointNext(ptr, &next);
+ ptr += next;
+ length++;
+ }
}
return length;
@@ -2164,11 +2201,14 @@ int GetCodepoint(const char *text, int *codepointSize)
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
*/
- // NOTE: on decode errors we return as soon as possible
+
int codepoint = 0x3f; // Codepoint (defaults to '?')
- int octet = (unsigned char)(text[0]); // The first UTF8 octet
*codepointSize = 1;
+ if (text == NULL) return codepoint;
+
+ // NOTE: on decode errors we return as soon as possible
+ int octet = (unsigned char)(text[0]); // The first UTF8 octet
if (octet <= 0x7f)
{
@@ -2260,6 +2300,7 @@ int GetCodepointNext(const char *text, int *codepointSize)
const char *ptr = text;
int codepoint = 0x3f; // Codepoint (defaults to '?')
*codepointSize = 1;
+ if (text == NULL) return codepoint;
// Get current codepoint and bytes processed
if (0xf0 == (0xf8 & ptr[0]))
@@ -2298,15 +2339,15 @@ int GetCodepointPrevious(const char *text, int *codepointSize)
{
const char *ptr = text;
int codepoint = 0x3f; // Codepoint (defaults to '?')
- int cpSize = 0;
- *codepointSize = 0;
+ *codepointSize = 1;
+ if (text == NULL) return codepoint;
// Move to previous codepoint
do ptr--;
while (((0x80 & ptr[0]) != 0) && ((0xc0 & ptr[0]) == 0x80));
+ int cpSize = 0;
codepoint = GetCodepointNext(ptr, &cpSize);
-
if (codepoint != 0) *codepointSize = cpSize;
return codepoint;
@@ -2444,7 +2485,7 @@ static Font LoadBMFont(const char *fileName)
RL_FREE(imFonts);
- if (isGpuReady) font.texture = LoadTextureFromImage(fullFont);
+ font.texture = LoadTextureFromImage(fullFont);
// Fill font characters info data
font.baseSize = fontSize;
@@ -2453,7 +2494,15 @@ static Font LoadBMFont(const char *fileName)
font.glyphs = (GlyphInfo *)RL_MALLOC(glyphCount*sizeof(GlyphInfo));
font.recs = (Rectangle *)RL_MALLOC(glyphCount*sizeof(Rectangle));
- int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX, pageID;
+ int charId = 0;
+ int charX = 0;
+ int charY = 0;
+ int charWidth = 0;
+ int charHeight = 0;
+ int charOffsetX = 0;
+ int charOffsetY = 0;
+ int charAdvanceX = 0;
+ int pageID = 0;
for (int i = 0; i < glyphCount; i++)
{
@@ -2486,7 +2535,7 @@ static Font LoadBMFont(const char *fileName)
UnloadImage(fullFont);
UnloadFileText(fileText);
- if (isGpuReady && (font.texture.id == 0))
+ if (font.texture.id == 0)
{
UnloadFont(font);
font = GetFontDefault();
@@ -2517,7 +2566,7 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, c
char buffer[MAX_BUFFER_SIZE] = { 0 };
GlyphInfo *glyphs = NULL;
- bool genFontChars = false;
+ bool internalCodepoints = false;
int totalReadBytes = 0; // Data bytes read (total)
int readBytes = 0; // Data bytes read (line)
@@ -2545,21 +2594,23 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, c
int charDWidthX = 0; // Character advance X
int charDWidthY = 0; // Character advance Y (unused)
- GlyphInfo *glyphs = NULL; // Pointer to output glyph info (NULL if not set)
- int *requiredCodepoints = codepoints;
+ int *requiredCodepoints = (int *)RL_MALLOC(codepointCount*sizeof(int));
if (fileData == NULL) return glyphs;
// In case no chars count provided, default to 95
codepointCount = (codepointCount > 0)? codepointCount : 95;
- // Fill fontChars in case not provided externally
- // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space)
- if (requiredCodepoints == NULL)
+ if (codepoints == NULL)
{
- requiredCodepoints = (int *)RL_MALLOC(codepointCount*sizeof(int));
+ // Fill internal codepoints array in case not provided externally
+ // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space)
for (int i = 0; i < codepointCount; i++) requiredCodepoints[i] = i + 32;
- genFontChars = true;
+ internalCodepoints = true;
+ }
+ else
+ {
+ for (int i = 0; i < codepointCount; i++) requiredCodepoints[i] = codepoints[i];
}
glyphs = (GlyphInfo *)RL_CALLOC(codepointCount, sizeof(GlyphInfo));
@@ -2634,11 +2685,11 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, c
// Search for glyph index in codepoints
glyphs = NULL;
- for (int codepointIndex = 0; codepointIndex < codepointCount; codepointIndex++)
+ for (int index = 0; index < codepointCount; index++)
{
- if (codepoints[codepointIndex] == charEncoding)
+ if (requiredCodepoints[index] == charEncoding)
{
- glyphs = &glyphs[codepointIndex];
+ glyphs = &glyphs[index];
break;
}
}
@@ -2738,7 +2789,7 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, c
}
}
- if (genFontChars) RL_FREE(codepoints);
+ RL_FREE(requiredCodepoints);
if (fontMalformed)
{
diff --git a/src/rtextures.c b/src/rtextures.c
index ddaee2939..4208b40bd 100644
--- a/src/rtextures.c
+++ b/src/rtextures.c
@@ -514,7 +514,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i
image.data = qoi_decode(fileData, dataSize, &desc, (int) fileData[12]);
image.width = desc.width;
image.height = desc.height;
- image.format = desc.channels == 4 ? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 : PIXELFORMAT_UNCOMPRESSED_R8G8B8;
+ image.format = (desc.channels == 4)? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 : PIXELFORMAT_UNCOMPRESSED_R8G8B8;
image.mipmaps = 1;
}
}
@@ -771,7 +771,7 @@ bool ExportImageAsCode(Image image, const char *fileName)
// Get file name from path and convert variable name to uppercase
char varFileName[256] = { 0 };
- strcpy(varFileName, GetFileNameWithoutExt(fileName));
+ strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1); // NOTE: Using function provided by [rcore] module
for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
// Add image information
@@ -1099,7 +1099,7 @@ Image GenImageCellular(int width, int height, int tileSize)
}
}
- // I made this up, but it seems to give good results at all tile sizes
+ // This approach seems to give good results at all tile sizes
int intensity = (int)(minDistance*256.0f/tileSize);
if (intensity > 255) intensity = 255;
@@ -1125,17 +1125,19 @@ Image GenImageCellular(int width, int height, int tileSize)
Image GenImageText(int width, int height, const char *text)
{
Image image = { 0 };
-
- int textLength = (int)strlen(text);
- int imageViewSize = width*height;
-
+
+ int imageSize = width*height;
image.width = width;
image.height = height;
image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
- image.data = RL_CALLOC(imageViewSize, 1);
+ image.data = RL_CALLOC(imageSize, 1);
image.mipmaps = 1;
- memcpy(image.data, text, (textLength > imageViewSize)? imageViewSize : textLength);
+ if (text != NULL)
+ {
+ int textLength = (int)strlen(text);
+ memcpy(image.data, text, (textLength > imageSize)? imageSize : textLength);
+ }
return image;
}
@@ -1484,8 +1486,9 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
{
Image imText = { 0 };
#if defined(SUPPORT_MODULE_RTEXT)
- int size = (int)strlen(text); // Get size in bytes of text
-
+ 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')
@@ -1496,7 +1499,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
// Create image to store text
imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK);
- for (int i = 0; i < size;)
+ for (int i = 0; i < textLength;)
{
// Get next codepoint from byte string and glyph index in font
int codepointByteCount = 0;
@@ -1724,7 +1727,8 @@ void ImageResizeNN(Image *image, int newWidth, int newHeight)
int xRatio = (int)((image->width << 16)/newWidth) + 1;
int yRatio = (int)((image->height << 16)/newHeight) + 1;
- int x2, y2;
+ int x2 = 0;
+ int y2 = 0;
for (int y = 0; y < newHeight; y++)
{
for (int x = 0; x < newWidth; x++)
@@ -2485,8 +2489,13 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
Color oldPixel = WHITE;
Color newPixel = WHITE;
- int rError, gError, bError;
- unsigned short rPixel, gPixel, bPixel, aPixel; // Used for 16bit pixel composition
+ int rError = 0;
+ int gError = 0;
+ int bError = 0;
+ unsigned short rPixel = 0; // Used for 16bit pixel composition
+ unsigned short gPixel = 0;
+ unsigned short bPixel = 0;
+ unsigned short aPixel = 0;
#define MIN(a,b) (((a)<(b))?(a):(b))
@@ -3341,11 +3350,14 @@ void ImageClearBackground(Image *dst, Color color)
unsigned char *pSrcPixel = (unsigned char *)dst->data;
int bytesPerPixel = GetPixelDataSize(1, 1, dst->format);
+ int totalPixels = dst->width*dst->height;
- // Repeat the first pixel data throughout the image
- for (int i = 1; i < dst->width*dst->height; i++)
+ // Repeat the first pixel data throughout the image,
+ // doubling the pixels copied on each iteration
+ for (int i = 1; i < totalPixels; i *= 2)
{
- memcpy(pSrcPixel + i*bytesPerPixel, pSrcPixel, bytesPerPixel);
+ int pixelsToCopy = MIN(i, totalPixels - i);
+ memcpy(pSrcPixel + i*bytesPerPixel, pSrcPixel, pixelsToCopy*bytesPerPixel);
}
}
@@ -3724,9 +3736,10 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color)
unsigned char *pSrcPixel = (unsigned char *)dst->data + bytesOffset;
// Repeat the first pixel data throughout the row
- for (int x = 1; x < (int)rec.width; x++)
+ for (int x = 1; x < (int)rec.width; x *= 2)
{
- memcpy(pSrcPixel + x*bytesPerPixel, pSrcPixel, bytesPerPixel);
+ int pixelsToCopy = MIN(x, (int)rec.width - x);
+ memcpy(pSrcPixel + x*bytesPerPixel, pSrcPixel, pixelsToCopy*bytesPerPixel);
}
// Repeat the first row data for all other rows
@@ -3997,11 +4010,11 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
// [x] Consider fast path: no alpha blending required cases (src has no alpha)
// [x] Consider fast path: same src/dst format with no alpha -> direct line copy
// [-] GetPixelColor(): Get Vector4 instead of Color, easier for ColorAlphaBlend()
- // [ ] Support f32bit channels drawing
+ // [ ] TODO: Support 16bit and 32bit (float) channels drawing
- // TODO: Support PIXELFORMAT_UNCOMPRESSED_R32G32B32A32 and PIXELFORMAT_UNCOMPRESSED_R1616B16A16
-
- Color colSrc, colDst, blend;
+ Color colSrc = { 0 };
+ Color colDst = { 0 };
+ Color blend = { 0 };
bool blendRequired = true;
// Fast path: Avoid blend if source has no alpha to blend
@@ -4197,8 +4210,8 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
}
/*else if (layout == CUBEMAP_LAYOUT_PANORAMA)
{
- // TODO: implement panorama by converting image to square faces...
- // Ref: https://github.com/denivip/panorama/blob/master/panorama.cpp
+ // TODO: Implement panorama by converting image to square faces...
+ // REF: https://github.com/denivip/panorama/blob/master/panorama.cpp
} */
else
{
@@ -4223,6 +4236,7 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
}
// Convert image data to 6 faces in a vertical column, that's the optimum layout for loading
+ // NOTE: Image formatting does not work with compressed textures
faces = GenImageColor(size, size*6, MAGENTA);
ImageFormat(&faces, image.format);
@@ -4235,8 +4249,6 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
}
#endif
- // NOTE: Image formatting does not work with compressed textures
-
for (int i = 0; i < 6; i++) ImageDraw(&faces, mipmapped, faceRecs[i], (Rectangle){ 0, (float)size*i, (float)size, (float)size }, WHITE);
UnloadImage(mipmapped);
@@ -4305,13 +4317,11 @@ bool IsTextureValid(Texture2D texture)
{
bool result = false;
- // TODO: Validate maximum texture size supported by GPU
-
if ((texture.id > 0) && // Validate OpenGL id (texture uplaoded to GPU)
(texture.width > 0) && // Validate texture width
(texture.height > 0) && // Validate texture height
(texture.format > 0) && // Validate texture pixel format
- (texture.mipmaps > 0)) result = true; // Validate texture mipmaps (at least 1 for basic mipmap level)
+ (texture.mipmaps > 0)) result = true; // Validate texture mipmaps (at least 1 for basic mipmap level)
return result;
}
@@ -4601,7 +4611,7 @@ void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2
// NOTE: Vertex position can be transformed using matrices
// but the process is way more costly than just calculating
// the vertex positions manually, like done above
- // I leave here the old implementation for educational purposes,
+ // Old implementation is left here for educational purposes,
// just in case someone wants to do some performance test
/*
rlSetTexture(texture.id);
@@ -4679,17 +4689,23 @@ void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest,
bottomBorder = patchHeight - topBorder;
}
- Vector2 vertA, vertB, vertC, vertD;
- vertA.x = 0.0f; // outer left
- vertA.y = 0.0f; // outer top
- vertB.x = leftBorder; // inner left
- vertB.y = topBorder; // inner top
- vertC.x = patchWidth - rightBorder; // inner right
- vertC.y = patchHeight - bottomBorder; // inner bottom
- vertD.x = patchWidth; // outer right
- vertD.y = patchHeight; // outer bottom
+ Vector2 vertA = { 0 };
+ Vector2 vertB = { 0 };
+ Vector2 vertC = { 0 };
+ Vector2 vertD = { 0 };
+ vertA.x = 0.0f; // Outer left
+ vertA.y = 0.0f; // Outer top
+ vertB.x = leftBorder; // Inner left
+ vertB.y = topBorder; // Inner top
+ vertC.x = patchWidth - rightBorder; // Inner right
+ vertC.y = patchHeight - bottomBorder; // Inner bottom
+ vertD.x = patchWidth; // Outer right
+ vertD.y = patchHeight; // Outer bottom
- Vector2 coordA, coordB, coordC, coordD;
+ Vector2 coordA = { 0 };
+ Vector2 coordB = { 0 };
+ Vector2 coordC = { 0 };
+ Vector2 coordD = { 0 };
coordA.x = nPatchInfo.source.x/width;
coordA.y = nPatchInfo.source.y/height;
coordB.x = (nPatchInfo.source.x + leftBorder)/width;
@@ -4905,7 +4921,9 @@ Vector3 ColorToHSV(Color color)
{
Vector3 hsv = { 0 };
Vector3 rgb = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
- float min, max, delta;
+ float min = 0.0f;
+ float max = 0.0f;
+ float delta = 0.0f;
min = rgb.x < rgb.y? rgb.x : rgb.y;
min = min < rgb.z? min : rgb.z;
diff --git a/src/utils.c b/src/utils.c
index 123c7b0b9..09158893a 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -105,7 +105,7 @@ void TraceLog(int logType, const char *text, ...)
{
#if defined(SUPPORT_TRACELOG)
// Message has level below current threshold, don't emit
- if (logType < logTypeLevel) return;
+ if ((logType < logTypeLevel) || (text == NULL)) return;
va_list args;
va_start(args, text);
@@ -142,8 +142,8 @@ void TraceLog(int logType, const char *text, ...)
default: break;
}
- unsigned int textSize = (unsigned int)strlen(text);
- memcpy(buffer + strlen(buffer), text, (textSize < (MAX_TRACELOG_MSG_LENGTH - 12))? textSize : (MAX_TRACELOG_MSG_LENGTH - 12));
+ 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);
@@ -313,7 +313,7 @@ bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileN
// Get file name from path
char varFileName[256] = { 0 };
- strcpy(varFileName, GetFileNameWithoutExt(fileName));
+ strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1);
for (int i = 0; varFileName[i] != '\0'; i++)
{
// Convert variable name to uppercase
@@ -446,15 +446,15 @@ void InitAssetManager(AAssetManager *manager, const char *dataPath)
}
// Replacement for fopen()
-// Ref: https://developer.android.com/ndk/reference/group/asset
+// REF: https://developer.android.com/ndk/reference/group/asset
FILE *android_fopen(const char *fileName, const char *mode)
{
if (mode[0] == 'w')
{
- // fopen() is mapped to android_fopen() that only grants read access to
+ // 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
+ // 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)
diff --git a/tools/rexm/VS2022/raylib/raylib.vcxproj.filters b/tools/rexm/VS2022/raylib/raylib.vcxproj.filters
new file mode 100644
index 000000000..b5f5536dc
--- /dev/null
+++ b/tools/rexm/VS2022/raylib/raylib.vcxproj.filters
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ external
+
+
+ external
+
+
+ external
+
+
+ external
+
+
+ external
+
+
+ external
+
+
+ external
+
+
+ external
+
+
+ external
+
+
+ external
+
+
+ external
+
+
+
+
+ {7c380c65-acd0-428f-83d9-70ef06f69b6d}
+
+
+
\ No newline at end of file
diff --git a/tools/rexm/examples_report_issues.md b/tools/rexm/reports/examples_issues.md
similarity index 66%
rename from tools/rexm/examples_report_issues.md
rename to tools/rexm/reports/examples_issues.md
index 8d24a251d..14e7a61c5 100644
--- a/tools/rexm/examples_report_issues.md
+++ b/tools/rexm/reports/examples_issues.md
@@ -21,10 +21,10 @@ Example elements validated:
| **EXAMPLE NAME** | [C] | [CAT]| [INFO]|[PNG]|[WPNG]| [RES]| [MK] |[MKWEB]| [VCX]| [SOL]|[RDME]|[JS] | [WOUT]|[WMETA]|
|:---------------------------------|:---:|:----:|:-----:|:---:|:----:|:----:|:----:|:-----:|:----:|:----:|:----:|:---:|:-----:|:-----:|
| core_highdpi_testbed | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
-| core_screen_recording | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
-| rlgl_standalone | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
-| rlgl_compute_shader | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
-| easings_testbed | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
-| raylib_opengl_interop | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ |
-| embedded_files_loading | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
-| web_basic_window | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| shaders_game_of_life | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ |
+| rlgl_standalone | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ |
+| rlgl_compute_shader | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ |
+| easings_testbed | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| raylib_opengl_interop | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ❌ | ✔ | ❌ | ✔ | ✔ |
+| embedded_files_loading | ✔ | ❌ | ❌ | ✔ | ✔ | ❌ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ |
+| web_basic_window | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
diff --git a/tools/rexm/reports/examples_testing_drm.md b/tools/rexm/reports/examples_testing_drm.md
new file mode 100644
index 000000000..dba721a10
--- /dev/null
+++ b/tools/rexm/reports/examples_testing_drm.md
@@ -0,0 +1,21 @@
+# EXAMPLES COLLECTION - TESTING REPORT
+
+## Tested Platform: DRM
+
+```
+Example automated testing elements validated:
+ - [CWARN] : Compilation WARNING messages
+ - [LWARN] : Log WARNING messages count
+ - [INIT] : Initialization
+ - [CLOSE] : Closing
+ - [ASSETS] : Assets loading
+ - [RLGL] : OpenGL-wrapped initialization
+ - [PLAT] : Platform initialization
+ - [FONT] : Font default initialization
+ - [TIMER] : Timer initialization
+```
+| **EXAMPLE NAME** | [CWARN] | [LWARN] | [INIT] | [CLOSE] | [ASSETS] | [RLGL] | [PLAT] | [FONT] | [TIMER] |
+|:---------------------------------|:-------:|:-------:|:------:|:-------:|:--------:|:------:|:------:|:------:|:-------:|
+| text_font_loading | 0 | 10 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| text_codepoints_loading | 0 | 1 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| models_animation_playing | 0 | 1 | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ |
diff --git a/tools/rexm/reports/examples_testing_linux.md b/tools/rexm/reports/examples_testing_linux.md
new file mode 100644
index 000000000..8f6c377ee
--- /dev/null
+++ b/tools/rexm/reports/examples_testing_linux.md
@@ -0,0 +1,22 @@
+# EXAMPLES COLLECTION - TESTING REPORT
+
+## Tested Platform: Linux
+
+```
+Example automated testing elements validated:
+ - [CWARN] : Compilation WARNING messages
+ - [LWARN] : Log WARNING messages count
+ - [INIT] : Initialization
+ - [CLOSE] : Closing
+ - [ASSETS] : Assets loading
+ - [RLGL] : OpenGL-wrapped initialization
+ - [PLAT] : Platform initialization
+ - [FONT] : Font default initialization
+ - [TIMER] : Timer initialization
+```
+| **EXAMPLE NAME** | [CWARN] | [LWARN] | [INIT] | [CLOSE] | [ASSETS] | [RLGL] | [PLAT] | [FONT] | [TIMER] |
+|:---------------------------------|:-------:|:-------:|:------:|:-------:|:--------:|:------:|:------:|:------:|:-------:|
+| text_font_loading | 0 | 10 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| text_codepoints_loading | 0 | 1 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| models_animation_playing | 0 | 1 | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ |
+
diff --git a/tools/rexm/reports/examples_testing_macos.md b/tools/rexm/reports/examples_testing_macos.md
new file mode 100644
index 000000000..1fa686ee6
--- /dev/null
+++ b/tools/rexm/reports/examples_testing_macos.md
@@ -0,0 +1,24 @@
+# EXAMPLES COLLECTION - TESTING REPORT
+
+## Tested Platform: macOS
+
+```
+Example automated testing elements validated:
+ - [CWARN] : Compilation WARNING messages
+ - [LWARN] : Log WARNING messages count
+ - [INIT] : Initialization
+ - [CLOSE] : Closing
+ - [ASSETS] : Assets loading
+ - [RLGL] : OpenGL-wrapped initialization
+ - [PLAT] : Platform initialization
+ - [FONT] : Font default initialization
+ - [TIMER] : Timer initialization
+```
+| **EXAMPLE NAME** | [CWARN] | [LWARN] | [INIT] | [CLOSE] | [ASSETS] | [RLGL] | [PLAT] | [FONT] | [TIMER] |
+|:---------------------------------|:-------:|:-------:|:------:|:-------:|:--------:|:------:|:------:|:------:|:-------:|
+| text_font_loading | 0 | 10 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| text_codepoints_loading | 0 | 1 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| models_animation_playing | 0 | 1 | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ |
+| shaders_palette_switch | 0 | 0 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+| shaders_hybrid_rendering | 0 | 4 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+
diff --git a/tools/rexm/reports/examples_testing_web.md b/tools/rexm/reports/examples_testing_web.md
new file mode 100644
index 000000000..1f6ed58b3
--- /dev/null
+++ b/tools/rexm/reports/examples_testing_web.md
@@ -0,0 +1,36 @@
+# EXAMPLES COLLECTION - TESTING REPORT
+
+## Tested Platform: Web
+
+```
+Example automated testing elements validated:
+ - [CWARN] : Compilation WARNING messages
+ - [LWARN] : Log WARNING messages count
+ - [INIT] : Initialization
+ - [CLOSE] : Closing
+ - [ASSETS] : Assets loading
+ - [RLGL] : OpenGL-wrapped initialization
+ - [PLAT] : Platform initialization
+ - [FONT] : Font default initialization
+ - [TIMER] : Timer initialization
+```
+| **EXAMPLE NAME** | [CWARN] | [LWARN] | [INIT] | [CLOSE] | [ASSETS] | [RLGL] | [PLAT] | [FONT] | [TIMER] |
+|:---------------------------------|:-------:|:-------:|:------:|:-------:|:--------:|:------:|:------:|:------:|:-------:|
+| core_monitor_detector | 0 | 1 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| textures_sprite_stacking | 0 | 0 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ |
+| text_sprite_fonts | 0 | 0 | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ |
+| text_font_loading | 0 | 3 | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ |
+| text_font_sdf | 0 | 22 | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ |
+| text_codepoints_loading | 0 | 1 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| models_animation_playing | 0 | 1 | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ |
+| models_mesh_generation | 0 | 0 | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| models_loading_gltf | 0 | 0 | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ |
+| models_bone_socket | 0 | 0 | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ |
+| models_decals | 0 | 0 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+| shaders_postprocessing | 0 | 0 | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ |
+| shaders_deferred_rendering | 0 | 2 | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| shaders_shadowmap_rendering | 0 | 0 | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ |
+| shaders_basic_pbr | 0 | 0 | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ❌ |
+| audio_module_playing | 0 | 1 | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ |
+| audio_fft_spectrum_visualizer | 0 | 0 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+
diff --git a/tools/rexm/reports/examples_testing_windows.md b/tools/rexm/reports/examples_testing_windows.md
new file mode 100644
index 000000000..8b8804bda
--- /dev/null
+++ b/tools/rexm/reports/examples_testing_windows.md
@@ -0,0 +1,22 @@
+# EXAMPLES COLLECTION - TESTING REPORT
+
+## Tested Platform: Windows
+
+```
+Example automated testing elements validated:
+ - [CWARN] : Compilation WARNING messages
+ - [LWARN] : Log WARNING messages count
+ - [INIT] : Initialization
+ - [CLOSE] : Closing
+ - [ASSETS] : Assets loading
+ - [RLGL] : OpenGL-wrapped initialization
+ - [PLAT] : Platform initialization
+ - [FONT] : Font default initialization
+ - [TIMER] : Timer initialization
+```
+| **EXAMPLE NAME** | [CWARN] | [LWARN] | [INIT] | [CLOSE] | [ASSETS] | [RLGL] | [PLAT] | [FONT] | [TIMER] |
+|:---------------------------------|:-------:|:-------:|:------:|:-------:|:--------:|:------:|:------:|:------:|:-------:|
+| text_font_loading | 0 | 10 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| text_codepoints_loading | 0 | 1 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| models_animation_playing | 0 | 1 | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ |
+
diff --git a/tools/rexm/examples_report.md b/tools/rexm/reports/examples_validation.md
similarity index 92%
rename from tools/rexm/examples_report.md
rename to tools/rexm/reports/examples_validation.md
index 7734a234c..45c195415 100644
--- a/tools/rexm/examples_report.md
+++ b/tools/rexm/reports/examples_validation.md
@@ -59,12 +59,14 @@ Example elements validated:
| core_high_dpi | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| core_render_texture | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| core_undo_redo | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| core_viewport_scaling | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| core_input_actions | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| core_directory_files | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| core_highdpi_testbed | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
-| core_screen_recording | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| core_screen_recording | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| core_clipboard_text | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| core_text_file_loading | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| core_compute_hash | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| shapes_basic_shapes | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| shapes_bouncing_ball | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| shapes_bullet_hell | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
@@ -99,6 +101,10 @@ Example elements validated:
| shapes_starfield_effect | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| shapes_lines_drawing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| shapes_math_angle_rotation | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| shapes_rlgl_color_wheel | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| shapes_rlgl_triangle | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| shapes_ball_physics | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| shapes_penrose_tile | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| textures_logo_raylib | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| textures_srcrec_dstrec | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| textures_image_drawing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
@@ -124,7 +130,10 @@ Example elements validated:
| textures_image_kernel | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| textures_image_channel | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| textures_image_rotate | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| textures_screen_buffer | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| textures_textured_curve | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| textures_sprite_stacking | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| textures_cellular_automata | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| text_sprite_fonts | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| text_font_spritefont | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| text_font_filters | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
@@ -140,6 +149,7 @@ Example elements validated:
| text_codepoints_loading | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| text_inline_styling | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| text_words_alignment | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| text_strings_management | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| models_animation_playing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| models_billboard_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| models_box_collisions | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
@@ -166,6 +176,7 @@ Example elements validated:
| models_basic_voxel | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| models_rotating_cube | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| models_decals | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| models_directional_billboard | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| shaders_ascii_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| shaders_basic_lighting | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| shaders_model_shader | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
@@ -198,6 +209,7 @@ Example elements validated:
| shaders_lightmap_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| shaders_rounded_rectangle | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| shaders_depth_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| shaders_game_of_life | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ |
| audio_module_playing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| audio_music_stream | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| audio_raw_stream | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
@@ -206,9 +218,10 @@ Example elements validated:
| audio_stream_effects | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| audio_sound_multi | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| audio_sound_positioning | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
-| rlgl_standalone | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
-| rlgl_compute_shader | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
-| easings_testbed | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
-| raylib_opengl_interop | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ |
-| embedded_files_loading | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
-| web_basic_window | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| audio_spectrum_visualizer | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| 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 f45ce9be3..aa491ff23 100644
--- a/tools/rexm/rexm.c
+++ b/tools/rexm/rexm.c
@@ -1,6 +1,6 @@
/*******************************************************************************************
*
-* rexm [raylib examples manager] - A simple command-line tool to manage raylib examples
+* rexm [raylib examples manager] - A simple and easy-to-use raylib examples collection manager
*
* Supported processes:
* - create
@@ -8,8 +8,9 @@
* - rename
* - remove
* - build
-* - validate
-* - update
+* - test
+* - validate // All examples
+* - update // All examples
*
* Files involved in the processes:
* - raylib/examples//_example_name.c
@@ -50,12 +51,12 @@
#include "raylib.h"
-#include
#include // Required for: rename(), remove()
#include // Required for: strcmp(), strcpy()
+#include // Required for: NULL, calloc(), free()
#define SUPPORT_LOG_INFO
-#if defined(SUPPORT_LOG_INFO) && defined(_DEBUG)
+#if defined(SUPPORT_LOG_INFO) //&& defined(_DEBUG)
#define LOG(...) printf("REXM: "__VA_ARGS__)
#else
#define LOG(...)
@@ -71,6 +72,12 @@
// Create local commit with changes on example renaming
//#define RENAME_AUTO_COMMIT_CREATION
+#if defined(__GNUC__) // GCC and Clang
+ #pragma GCC diagnostic push
+ // Avoid GCC/Clang complaining about sprintf() second parameter not being a string literal (being TextFormat())
+ #pragma GCC diagnostic ignored "-Wformat-security"
+#endif
+
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
@@ -86,11 +93,18 @@ typedef struct {
char author[64]; // Example author
char authorGitHub[64]; // Example author, GitHub user name
- int status; // Example validation status info
+ int status; // Example validation status flags
int resCount; // Example resources counter
char **resPaths; // Example resources paths (MAX: 256)
} rlExampleInfo;
+// Automated testing data
+typedef struct {
+ int buildwarns; // Example building warnings count (by GCC compiler)
+ int warnings; // Example run output log warnings count
+ int status; // Example run testing status flags (>0 = FAILS)
+} rlExampleTesting;
+
// Validation status for a single example
typedef enum {
VALID_OK = 0, // All required files and entries are present
@@ -111,6 +125,18 @@ typedef enum {
VALID_UNKNOWN_ERROR = 1 << 14 // Unknown failure case (fallback)
} rlExampleValidationStatus;
+typedef enum {
+ TESTING_OK = 0, // All automated testing ok
+ TESTING_FAIL_INIT = 1 << 0, // Initialization (InitWindow()) -> "INFO: DISPLAY: Device initialized successfully"
+ TESTING_FAIL_CLOSE = 1 << 1, // Closing (CloseWindow()) -> "INFO: Window closed successfully"
+ TESTING_FAIL_ASSETS = 1 << 2, // Assets loading (WARNING: FILE:) -> "WARNING: FILEIO:"
+ TESTING_FAIL_RLGL = 1 << 3, // OpenGL-wrapped initialization -> "INFO: RLGL: Default OpenGL state initialized successfully"
+ TESTING_FAIL_PLATFORM = 1 << 4, // Platform initialization -> "INFO: PLATFORM: DESKTOP (GLFW - Win32): Initialized successfully"
+ TESTING_FAIL_FONT = 1 << 5, // Font deefault initialization -> "INFO: FONT: Default font loaded successfully (224 glyphs)"
+ TESTING_FAIL_TIMER = 1 << 6, // Timer initialization -> "INFO: TIMER: Target time per frame: 16.667 milliseconds"
+ TESTING_FAIL_OTHER = 1 << 7, // Other types of warnings (WARNING:)
+} rlExampleTestingStatus;
+
// Example management operations
typedef enum {
OP_NONE = 0, // No process to do
@@ -119,8 +145,10 @@ typedef enum {
OP_RENAME = 3, // Rename existing example
OP_REMOVE = 4, // Remove existing example
OP_VALIDATE = 5, // Validate examples, using [examples_list.txt] as main source by default
- OP_UPDATE = 6, // Validate and update required examples (as far as possible)
- OP_BUILD = 7, // Build example for desktop and web, copy web output
+ OP_UPDATE = 6, // Validate and update required examples (as far as possible): ALL
+ OP_BUILD = 7, // Build example(s) for desktop and web, copy web output - Multiple examples supported
+ OP_TEST = 8, // Test example(s), checking output log "WARNING" - Multiple examples supported
+ OP_TESTLOG = 9, // Process available examples logs to generate report
} rlExampleOperation;
static const char *exCategories[REXM_MAX_EXAMPLE_CATEGORIES] = { "core", "shapes", "textures", "text", "models", "shaders", "audio", "others" };
@@ -144,8 +172,8 @@ static int UpdateRequiredFiles(void);
// Load examples collection information
// NOTE 1: Load by category: "ALL", "core", "shapes", "textures", "text", "models", "shaders", others"
// NOTE 2: Sort examples list on request flag
-static rlExampleInfo *LoadExamplesData(const char *fileName, const char *category, bool sort, int *exCount);
-static void UnloadExamplesData(rlExampleInfo *exInfo);
+static rlExampleInfo *LoadExampleData(const char *filter, bool sort, int *exCount);
+static void UnloadExampleData(rlExampleInfo *exInfo);
// Load example info from file header
static rlExampleInfo *LoadExampleInfo(const char *exFileName);
@@ -160,10 +188,10 @@ static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry);
static void SortExampleByName(rlExampleInfo *items, int count);
// Scan resource paths in example file
-static char **ScanExampleResources(const char *filePath, int *resPathCount);
+static char **LoadExampleResourcePaths(const char *filePath, int *resPathCount);
// Clear resource paths scanned
-static void ClearExampleResources(char **resPaths);
+static void UnloadExampleResourcePaths(char **resPaths);
// Add/remove VS project (.vcxproj) tofrom existing VS solution (.sln)
static int AddVSProjectToSolution(const char *slnFile, const char *projFile, const char *category);
@@ -221,7 +249,8 @@ int main(int argc, char *argv[])
char exRecategory[32] = { 0 }; // Example re-name category: shapes
char exRename[64] = { 0 }; // Example re-name, without extension
- char exRebuildRequested[16] = { 0 }; // Example category/full rebuild request
+ char *exBuildList[256] = { 0 }; // Example build list for: ALL, , single-example
+ int exBuildListCount = 0; // Example build list file count
int opCode = OP_NONE; // Operation code: 0-None(Help), 1-Create, 2-Add, 3-Rename, 4-Remove
bool showUsage = false; // Flag to show usage help
@@ -381,35 +410,39 @@ int main(int argc, char *argv[])
opCode = OP_UPDATE;
}
- else if (strcmp(argv[1], "build") == 0)
+ else if ((strcmp(argv[1], "build") == 0) || (strcmp(argv[1], "test") == 0) || (strcmp(argv[1], "testlog") == 0))
{
- // Build example for PLATFORM_DESKTOP and PLATFORM_WEB
+ // Build/Test example(s) for PLATFORM_DESKTOP and PLATFORM_WEB
// NOTE: Build outputs to default directory, usually where the .c file is located,
// to avoid issues with copying resources (at least on Desktop)
// Web build files (.html, .wasm, .js, .data) are copied to raylib.com/examples repo
// Check for valid upcoming argument
- if (argc == 2) LOG("WARNING: No example name provided to build\n");
+ if (argc == 2) LOG("WARNING: No example name/category provided\n");
else if (argc > 3) LOG("WARNING: Too many arguments provided\n");
else
{
- // Support building not only individual examples but categories and "ALL"
- if ((strcmp(argv[2], "ALL") == 0) || TextInList(argv[2], exCategories, REXM_MAX_EXAMPLE_CATEGORIES))
+ // Support building/testing not only individual examples but multiple: ALL/
+ int exBuildListInfoCount = 0;
+ rlExampleInfo *exBuildListInfo = LoadExampleData(argv[2], false, &exBuildListInfoCount);
+
+ for (int i = 0; i < exBuildListInfoCount; i++)
{
- // Category/ALL rebuilt requested
- strcpy(exRebuildRequested, argv[2]);
+ if (!TextIsEqual(exBuildListInfo[i].category, "others"))
+ {
+ exBuildList[exBuildListCount] = (char *)RL_CALLOC(256, sizeof(char));
+ strcpy(exBuildList[exBuildListCount], exBuildListInfo[i].name);
+ exBuildListCount++;
+ }
}
+
+ UnloadExampleData(exBuildListInfo);
+
+ if (exBuildListCount == 0) LOG("WARNING: BUILD: Example requested not available in the collection\n");
else
{
- // Verify example exists in collection to be removed
- char *exColInfo = LoadFileText(exCollectionFilePath);
- if (TextFindIndex(exColInfo, argv[2]) != -1) // Example in the collection
- {
- strcpy(exName, argv[2]); // Register example name for removal
- strncpy(exCategory, exName, TextFindIndex(exName, "_"));
- opCode = OP_BUILD;
- }
- else LOG("WARNING: BUILD: Example requested not available in the collection\n");
- UnloadFileText(exColInfo);
+ if (strcmp(argv[1], "build") == 0) opCode = OP_BUILD;
+ else if (strcmp(argv[1], "test") == 0) opCode = OP_TEST;
+ else if (strcmp(argv[1], "testlog") == 0) opCode = OP_TESTLOG;
}
}
}
@@ -417,14 +450,8 @@ int main(int argc, char *argv[])
// Process command line options arguments
for (int i = 1; i < argc; i++)
{
- if ((strcmp(argv[i], "-h") == 0) || (strcmp(argv[i], "--help") == 0))
- {
- showUsage = true;
- }
- else if ((strcmp(argv[i], "-v") == 0) || (strcmp(argv[i], "--verbose") == 0))
- {
- verbose = true;
- }
+ if ((strcmp(argv[i], "-h") == 0) || (strcmp(argv[i], "--help") == 0)) showUsage = true;
+ else if ((strcmp(argv[i], "-v") == 0) || (strcmp(argv[i], "--verbose") == 0)) verbose = true;
}
}
@@ -481,7 +508,7 @@ int main(int argc, char *argv[])
// NOTE: resources path will be relative to example source file directory
int resPathCount = 0;
LOG("INFO: [%s] Scanning file for resources...\n", GetFileName(inFileName));
- char **resPaths = ScanExampleResources(TextFormat("%s/%s.c", GetDirectoryPath(inFileName), exName), &resPathCount);
+ char **resPaths = LoadExampleResourcePaths(TextFormat("%s/%s.c", GetDirectoryPath(inFileName), exName), &resPathCount);
if (resPathCount > 0)
{
@@ -539,7 +566,7 @@ int main(int argc, char *argv[])
}
}
- ClearExampleResources(resPaths);
+ UnloadExampleResourcePaths(resPaths);
// -----------------------------------------------------------------------------------------
// Add example to the collection list, if not already there
@@ -695,13 +722,13 @@ int main(int argc, char *argv[])
// Edit: Update example source code metadata
int exListCount = 0;
- rlExampleInfo *exList = LoadExamplesData(exCollectionFilePath, exCategory, false, &exListCount);
+ rlExampleInfo *exList = LoadExampleData(exCategory, false, &exListCount);
for (int i = 0; i < exListCount; i++)
{
if (strcmp(exList[i].name, exRename) == 0)
UpdateSourceMetadata(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exRename), &exList[i]);
}
- UnloadExamplesData(exList);
+ UnloadExampleData(exList);
// NOTE: Example resource files do not need to be changed...
// unless the example is moved from one caegory to another
@@ -887,64 +914,71 @@ int main(int argc, char *argv[])
case OP_BUILD:
{
LOG("INFO: Command requested: BUILD\n");
- LOG("INFO: Example to be built: %s\n", exRebuildRequested);
+ LOG("INFO: Example(s) to be built: %i [%s]\n", exBuildListCount, (exBuildListCount == 1)? exBuildList[0] : argv[2]);
- if ((strcmp(exRebuildRequested, "others") != 0) &&
- (strcmp(exCategory, "others") != 0)) // Skipping "others" category for rebuild: Special needs
+#if defined(_WIN32)
+ // Set required environment variables
+ //putenv(TextFormat("RAYLIB_DIR=%s\\..", exBasePath));
+ _putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin");
+ //putenv("MAKE=mingw32-make");
+ //ChangeDirectory(exBasePath);
+#endif
+ for (int i = 0; i < exBuildListCount; i++)
{
- int exRebuildCount = 0;
- rlExampleInfo *exRebuildList = LoadExamplesData(exCollectionFilePath, exRebuildRequested, false, &exRebuildCount);
+ // Get example name and category
+ memset(exName, 0, 64);
+ strcpy(exName, exBuildList[i]);
+ memset(exCategory, 0, 32);
+ strncpy(exCategory, exName, TextFindIndex(exName, "_"));
+ LOG("INFO: [%i/%i] Building example: [%s]\n", i + 1, exBuildListCount, exName);
+
+ // Build example for PLATFORM_DESKTOP
+#if defined(_WIN32)
+ LOG("INFO: [%s] Building example for PLATFORM_DESKTOP (Host: Win32)\n", exName);
+ system(TextFormat("mingw32-make -C %s %s/%s PLATFORM=PLATFORM_DESKTOP -B", exBasePath, exCategory, exName));
+#elif defined(PLATFORM_DRM)
+ LOG("INFO: [%s] Building example for PLATFORM_DRM (Host: POSIX)\n", exName);
+ system(TextFormat("make -C %s %s/%s PLATFORM=PLATFORM_DRM -B > %s/%s/logs/%s.build.log 2>&1",
+ exBasePath, exCategory, exName, exBasePath, exCategory, exName));
+#else
+ LOG("INFO: [%s] Building example for PLATFORM_DESKTOP (Host: POSIX)\n", exName);
+ system(TextFormat("make -C %s %s/%s PLATFORM=PLATFORM_DESKTOP -B", exBasePath, exCategory, exName));
+#endif
+
+#if !defined(PLATFORM_DRM)
+ // Build example for PLATFORM_WEB
// Build: raylib.com/examples//_example_name.html
// Build: raylib.com/examples//_example_name.data
// Build: raylib.com/examples//_example_name.wasm
// Build: raylib.com/examples//_example_name.js
-#if defined(_WIN32)
- // Set required environment variables
- //putenv(TextFormat("RAYLIB_DIR=%s\\..", exBasePath));
- _putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin");
- //putenv("MAKE=mingw32-make");
- //ChangeDirectory(exBasePath);
-#endif
- for (int i = 0; i < exRebuildCount; i++)
- {
- // Build example for PLATFORM_DESKTOP
-#if defined(_WIN32)
- LOG("INFO: [%s] Building example for PLATFORM_DESKTOP (Host: Win32)\n", exName);
- system(TextFormat("mingw32-make -C %s %s/%s PLATFORM=PLATFORM_DESKTOP -B", exBasePath, exRebuildList[i].category, exRebuildList[i].name));
-#else
- LOG("INFO: [%s] Building example for PLATFORM_DESKTOP (Host: POSIX)\n", exName);
- system(TextFormat("make -C %s %s/%s PLATFORM=PLATFORM_DESKTOP -B", exBasePath, exRebuildList[i].category, exRebuildList[i].name));
-#endif
+ #if defined(_WIN32)
+ LOG("INFO: [%s] Building example for PLATFORM_WEB (Host: Win32)\n", exName);
+ system(TextFormat("mingw32-make -C %s -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B", exBasePath, exCategory, exName));
+ #else
+ LOG("INFO: [%s] Building example for PLATFORM_WEB (Host: POSIX)\n", exName);
+ system(TextFormat("make -C %s -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B", exBasePath, exCategory, exName));
+ #endif
+ // Update generated .html metadata
+ LOG("INFO: [%s] Updating HTML Metadata...\n", TextFormat("%s.html", exName));
+ UpdateWebMetadata(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName),
+ TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
- // Build example for PLATFORM_WEB
-#if defined(_WIN32)
- LOG("INFO: [%s] Building example for PLATFORM_WEB (Host: Win32)\n", exName);
- system(TextFormat("mingw32-make -C %s -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B", exBasePath, exRebuildList[i].category, exRebuildList[i].name));
-#else
- LOG("INFO: [%s] Building example for PLATFORM_WEB (Host: POSIX)\n", exName);
- system(TextFormat("make -C %s -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B", exBasePath, exRebuildList[i].category, exRebuildList[i].name));
-#endif
- // Update generated .html metadata
- LOG("INFO: [%s] Updating HTML Metadata...\n", TextFormat("%s.html", exRebuildList[i].name));
- UpdateWebMetadata(TextFormat("%s/%s/%s.html", exBasePath, exRebuildList[i].category, exRebuildList[i].name),
- TextFormat("%s/%s/%s.c", exBasePath, exRebuildList[i].category, exRebuildList[i].name));
+ // Copy results to web side
+ LOG("INFO: [%s] Copy example build to raylib.com\n", exName);
+ FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName),
+ TextFormat("%s/%s/%s.html", exWebPath, exCategory, exName));
+ FileCopy(TextFormat("%s/%s/%s.data", exBasePath, exCategory, exName),
+ TextFormat("%s/%s/%s.data", exWebPath, exCategory, exName));
+ FileCopy(TextFormat("%s/%s/%s.wasm", exBasePath, exCategory, exName),
+ TextFormat("%s/%s/%s.wasm", exWebPath, exCategory, exName));
+ FileCopy(TextFormat("%s/%s/%s.js", exBasePath, exCategory, exName),
+ TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName));
+#endif // !PLATFORM_DRM
- // Copy results to web side
- LOG("INFO: [%s] Copy example build to raylib.com\n", exRebuildList[i].name);
- FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exRebuildList[i].category, exRebuildList[i].name),
- TextFormat("%s/%s/%s.html", exWebPath, exRebuildList[i].category, exRebuildList[i].name));
- FileCopy(TextFormat("%s/%s/%s.data", exBasePath, exRebuildList[i].category, exRebuildList[i].name),
- TextFormat("%s/%s/%s.data", exWebPath, exRebuildList[i].category, exRebuildList[i].name));
- FileCopy(TextFormat("%s/%s/%s.wasm", exBasePath, exRebuildList[i].category, exRebuildList[i].name),
- TextFormat("%s/%s/%s.wasm", exWebPath, exRebuildList[i].category, exRebuildList[i].name));
- FileCopy(TextFormat("%s/%s/%s.js", exBasePath, exRebuildList[i].category, exRebuildList[i].name),
- TextFormat("%s/%s/%s.js", exWebPath, exRebuildList[i].category, exRebuildList[i].name));
- }
-
- UnloadExamplesData(exRebuildList);
+ // Once example processed, free memory from list
+ RL_FREE(exBuildList[i]);
}
- else LOG("WARNING: [others] category examples should be build manually, they could have specific build requirements\n");
} break;
case OP_VALIDATE: // Validate: report and actions
@@ -974,6 +1008,7 @@ int main(int argc, char *argv[])
LOG("INFO: Scanning available example (.c) files to be added to collection...\n");
FilePathList clist = LoadDirectoryFilesEx(exBasePath, ".c", true);
+ // Load examples collection list file (raylib/examples/examples_list.txt)
char *exList = LoadFileText(exCollectionFilePath);
char *exListUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1);
bool listUpdated = false;
@@ -990,7 +1025,7 @@ int main(int argc, char *argv[])
for (int i = 1; i < lineCount; i++)
{
- if ((TextFindIndex(exListUpdated, exListLines[i]) == -1) || (exListLines[i][0] == '#'))
+ if ((TextFindIndex(exListUpdated, exListLines[i]) == -1) || (exListLines[i][0] == '#'))
exListUpdatedOffset += sprintf(exListUpdated + exListUpdatedOffset, "%s\n", exListLines[i]);
else listUpdated = true;
}
@@ -1047,7 +1082,7 @@ int main(int argc, char *argv[])
// Check all examples in collection [examples_list.txt] -> Source of truth!
LOG("INFO: Validating examples in collection...\n");
int exCollectionCount = 0;
- rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, "ALL", false, &exCollectionCount);
+ rlExampleInfo *exCollection = LoadExampleData("ALL", false, &exCollectionCount);
// Set status information for all examples, using "status" field in the struct
for (int i = 0; i < exCollectionCount; i++)
@@ -1092,7 +1127,7 @@ int main(int argc, char *argv[])
// Validate: raylib/examples//resources/.. -> Example resources available?
// Scan resources used in example to check for missing resource files
// WARNING: Some paths could be for files to save, not files to load, verify it
- char **resPaths = ScanExampleResources(TextFormat("%s/%s/%s.c", exBasePath, exInfo->category, exInfo->name), &exInfo->resCount);
+ char **resPaths = LoadExampleResourcePaths(TextFormat("%s/%s/%s.c", exBasePath, exInfo->category, exInfo->name), &exInfo->resCount);
if (exInfo->resCount > 0)
{
for (int r = 0; r < exInfo->resCount; r++)
@@ -1125,7 +1160,7 @@ int main(int argc, char *argv[])
}
}
}
- ClearExampleResources(resPaths);
+ UnloadExampleResourcePaths(resPaths);
// Validate: raylib.com/examples//_example_name.html -> File exists?
// Validate: raylib.com/examples//_example_name.data -> File exists?
@@ -1329,7 +1364,7 @@ int main(int argc, char *argv[])
| shapes_colors_palette | ✘ | ✔ | ✘ | ✔ | ✘ | ✔ | ✔ | ✘ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| text_format_text | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ | ✔ | ✘ | ✔ | ✔ | ✔ | ✔ |
*/
- LOG("INFO: [examples_report.md] Generating examples validation report...\n");
+ LOG("INFO: [examples_validation.md] Generating examples validation report...\n");
char *report = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1);
@@ -1375,13 +1410,13 @@ int main(int argc, char *argv[])
(exCollection[i].status & VALID_MISSING_WEB_METADATA)? "❌" : "✔");
}
- SaveFileText(TextFormat("%s/../tools/rexm/%s", exBasePath, "examples_report.md"), report);
+ SaveFileText(TextFormat("%s/../tools/rexm/reports/%s", exBasePath, "examples_validation.md"), report);
RL_FREE(report);
//-----------------------------------------------------------------------------------------------------
// Generate a report with only the examples missing some elements
//-----------------------------------------------------------------------------------------------------
- LOG("INFO: [examples_report_issues.md] Generating examples issues report...\n");
+ LOG("INFO: [examples_issues.md] Generating examples issues report...\n");
char *reportIssues = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1);
@@ -1430,13 +1465,382 @@ int main(int argc, char *argv[])
}
}
- SaveFileText(TextFormat("%s/../tools/rexm/%s", exBasePath, "examples_report_issues.md"), reportIssues);
+ SaveFileText(TextFormat("%s/../tools/rexm/reports/%s", exBasePath, "examples_issues.md"), reportIssues);
RL_FREE(reportIssues);
//-----------------------------------------------------------------------------------------------------
- UnloadExamplesData(exCollection);
+ UnloadExampleData(exCollection);
//------------------------------------------------------------------------------------------------
+ } break;
+ case OP_TEST:
+ {
+ LOG("INFO: Command requested: TEST\n");
+ LOG("INFO: Example(s) to be build and tested: %i [%s]\n", exBuildListCount, (exBuildListCount == 1)? exBuildList[0] : argv[2]);
+
+#if defined(_WIN32)
+ // Set required environment variables
+ //putenv(TextFormat("RAYLIB_DIR=%s\\..", exBasePath));
+ //_putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin");
+ //putenv("MAKE=mingw32-make");
+ //ChangeDirectory(exBasePath);
+ //_putenv("MAKE_PATH=C:\\raylib\\w64devkit\\bin");
+ //_putenv("EMSDK_PATH = C:\\raylib\\emsdk");
+ //_putenv("PYTHON_PATH=$(EMSDK_PATH)\\python\\3.9.2-nuget_64bit");
+ //_putenv("NODE_PATH=$(EMSDK_PATH)\\node\\20.18.0_64bit\\bin");
+ //_putenv("PATH=%PATH%;$(MAKE_PATH);$(EMSDK_PATH);$(NODE_PATH);$(PYTHON_PATH)");
+
+ _putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin;C:\\raylib\\emsdk\\python\\3.9.2-nuget_64bit;C:\\raylib\\emsdk\\node\\20.18.0_64bit\\bin");
+#endif
+
+ for (int i = 0; i < exBuildListCount; i++)
+ {
+ // Get example name and category
+ memset(exName, 0, 64);
+ strcpy(exName, exBuildList[i]);
+ memset(exCategory, 0, 32);
+ strncpy(exCategory, exName, TextFindIndex(exName, "_"));
+
+ // Skip some examples from building
+ if ((strcmp(exName, "core_custom_logging") == 0) ||
+ (strcmp(exName, "core_window_should_close") == 0) ||
+ (strcmp(exName, "core_custom_frame_control") == 0)) continue;
+
+ LOG("INFO: [%i/%i] Testing example: [%s]\n", i + 1, exBuildListCount, exName);
+
+ // Create directory for logs (build and run logs)
+ MakeDirectory(TextFormat("%s/%s/logs", exBasePath, exCategory));
+
+ // Steps to follow
+ // STEP 1: Load example.c and replace required code to inject basic testing code: frames to run
+ // OPTION 1: Code injection required multiple changes for testing but it does not require raylib changes!
+ // OPTION 2: Support testing on raylib side: Args processing and events injection: SUPPORT_AUTOMATD_TESTING_SYSTEM, EVENTS_TESTING_MODE
+ // STEP 2: Build example (PLATFORM_DESKTOP)
+ // STEP 3: Run example with arguments: --frames 2 > .out.log
+ // STEP 4: Load .out.log and check "WARNING:" messages -> Some could maybe be ignored
+ // STEP 5: Generate report with results
+
+ // STEP 1: Load example and inject required code
+ // PROBLEM: As we need to modify the example source code for building, we need to keep a copy or something
+ // WARNING: If we make a copy and something fails, it could not be restored at the end
+ // PROBLEM: Trying to build a copy won't work because Makefile is setup to look for specific example on specific path -> No output dir config
+ // IDEA: Create directory for testing data -> It implies moving files and set working dir...
+ // SOLUTION: Make a copy of original file -> Modify original -> Build -> Rename to .test.exe
+ FileCopy(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName),
+ TextFormat("%s/%s/%s.original.c", exBasePath, exCategory, exName));
+ char *srcText = LoadFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
+
+//#define BUILD_TESTING_WEB
+#if defined(BUILD_TESTING_WEB)
+ static const char *mainReplaceText =
+ "#include \n"
+ "#include \n"
+ "#include \n"
+ "#include \n\n"
+ "static char logText[4096] = {0};\n"
+ "static int logTextOffset = 0;\n\n"
+ "void CustomTraceLog(int msgType, const char *text, va_list args)\n{\n"
+ " if (logTextOffset < 3800)\n {\n"
+ " switch (msgType)\n {\n"
+ " case LOG_INFO: logTextOffset += sprintf(logText + logTextOffset, \"INFO: \"); break;\n"
+ " case LOG_ERROR: logTextOffset += sprintf(logText + logTextOffset, \"ERROR: \"); break;\n"
+ " case LOG_WARNING: logTextOffset += sprintf(logText + logTextOffset, \"WARNING: \"); break;\n"
+ " case LOG_DEBUG: logTextOffset += sprintf(logText + logTextOffset, \"DEBUG: \"); break;\n"
+ " default: break;\n }\n"
+ " logTextOffset += vsprintf(logText + logTextOffset, text, args);\n"
+ " logTextOffset += sprintf(logText + logTextOffset, \"\\n\");\n}\n}\n\n"
+ "int main(int argc, char *argv[])\n{\n"
+ " SetTraceLogCallback(CustomTraceLog);\n"
+ " int requestedTestFrames = 0;\n"
+ " int testFramesCount = 0;\n"
+ " if ((argc > 1) && (argc == 3) && (strcmp(argv[1], \"--frames\") != 0)) requestedTestFrames = atoi(argv[2]);\n";
+
+ static const char *returnReplaceText =
+ " SaveFileText(\"outputLogFileName\", logText);\n"
+ " emscripten_run_script(\"saveFileFromMEMFSToDisk('outputLogFileName','outputLogFileName')\");\n\n"
+ " return 0";
+ char *returnReplaceTextUpdated = TextReplace(returnReplaceText, "outputLogFileName", TextFormat("%s.log", exName));
+
+ char *srcTextUpdated[4] = { 0 };
+ srcTextUpdated[0] = TextReplace(srcText, "int main(void)\n{", mainReplaceText);
+ srcTextUpdated[1] = TextReplace(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)");
+ srcTextUpdated[2] = TextReplace(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;");
+ srcTextUpdated[3] = TextReplace(srcTextUpdated[2], " return 0", returnReplaceTextUpdated);
+ MemFree(returnReplaceTextUpdated);
+ UnloadFileText(srcText);
+
+ SaveFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), srcTextUpdated[3]);
+ for (int i = 0; i < 4; i++) { MemFree(srcTextUpdated[i]); srcTextUpdated[i] = NULL; }
+
+ // Build example for PLATFORM_WEB
+ // Build: raylib.com/examples//_example_name.html
+ // Build: raylib.com/examples//_example_name.data
+ // Build: raylib.com/examples//_example_name.wasm
+ // Build: raylib.com/examples//_example_name.js
+ #if defined(_WIN32)
+ LOG("INFO: [%s] Building example for PLATFORM_WEB (Host: Win32)\n", exName);
+ system(TextFormat("mingw32-make -C %s -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B > %s/%s/logs/%s.build.log 2>&1",
+ exBasePath, exCategory, exName, exBasePath, exCategory, exName));
+ #else
+ LOG("INFO: [%s] Building example for PLATFORM_WEB (Host: POSIX)\n", exName);
+ system(TextFormat("make -C %s -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B", exBasePath, exCategory, exName));
+ #endif
+ // Restore original source code before continue
+ FileCopy(TextFormat("%s/%s/%s.original.c", exBasePath, exCategory, exName),
+ TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
+ 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));
+
+ // NOTE: Example .log is automatically downloaded into system Downloads directory on browser-example exectution
+
+#else // BUILD_TESTING_DESKTOP
+
+ static const char *mainReplaceText =
+ "#include \n"
+ "#include \n"
+ "int main(int argc, char *argv[])\n{\n"
+ " int requestedTestFrames = 0;\n"
+ " int testFramesCount = 0;\n"
+ " if ((argc > 1) && (argc == 3) && (strcmp(argv[1], \"--frames\") != 0)) requestedTestFrames = atoi(argv[2]);\n";
+
+ char *srcTextUpdated[3] = { 0 };
+ srcTextUpdated[0] = TextReplace(srcText, "int main(void)\n{", mainReplaceText);
+ srcTextUpdated[1] = TextReplace(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)");
+ srcTextUpdated[2] = TextReplace(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;");
+ UnloadFileText(srcText);
+
+ SaveFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), srcTextUpdated[2]);
+ for (int i = 0; i < 3; i++) { MemFree(srcTextUpdated[i]); srcTextUpdated[i] = NULL; }
+
+ // STEP 2: Build example for DESKTOP platform
+ #if defined(_WIN32)
+ // Set required environment variables
+ //putenv(TextFormat("RAYLIB_DIR=%s\\..", exBasePath));
+ _putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin");
+ //putenv("MAKE=mingw32-make");
+ //ChangeDirectory(exBasePath);
+ #endif
+ // Build example for PLATFORM_DESKTOP
+ #if defined(_WIN32)
+ LOG("INFO: [%s] Building example for PLATFORM_DESKTOP (Host: Win32)\n", exName);
+ system(TextFormat("mingw32-make -C %s %s/%s PLATFORM=PLATFORM_DESKTOP -B > %s/%s/logs/%s.build.log 2>&1",
+ exBasePath, exCategory, exName, exBasePath, exCategory, exName));
+ #elif defined(PLATFORM_DRM)
+ LOG("INFO: [%s] Building example for PLATFORM_DRM (Host: POSIX)\n", exName);
+ system(TextFormat("make -C %s %s/%s PLATFORM=PLATFORM_DRM -B > %s/%s/logs/%s.build.log 2>&1",
+ exBasePath, exCategory, exName, exBasePath, exCategory, exName));
+ #else
+ LOG("INFO: [%s] Building example for PLATFORM_DESKTOP (Host: POSIX)\n", exName);
+ system(TextFormat("make -C %s %s/%s PLATFORM=PLATFORM_DESKTOP -B > %s/%s/logs/%s.build.log 2>&1",
+ exBasePath, exCategory, exName, exBasePath, exCategory, exName));
+ #endif
+ // Restore original source code before continue
+ FileCopy(TextFormat("%s/%s/%s.original.c", exBasePath, exCategory, exName),
+ TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
+ FileRemove(TextFormat("%s/%s/%s.original.c", exBasePath, exCategory, exName));
+
+ // STEP 3: Run example with required arguments
+ // NOTE: Not easy to retrieve process return value from system(), it's platform dependant
+ ChangeDirectory(TextFormat("%s/%s", exBasePath, exCategory));
+
+ #if defined(_WIN32)
+ system(TextFormat("%s --frames 2 > logs/%s.log", exName, exName));
+ #else
+ system(TextFormat("./%s --frames 2 > logs/%s.log", exName, exName));
+ #endif
+#endif
+ }
+ } break;
+ case OP_TESTLOG:
+ {
+ // STEP 4: Load and validate available logs info
+ //---------------------------------------------------------------------------------------------
+ rlExampleTesting *testing = (rlExampleTesting *)RL_CALLOC(exBuildListCount, sizeof(rlExampleTesting));
+
+ for (int i = 0; i < exBuildListCount; i++)
+ {
+ // Get example name and category
+ memset(exName, 0, 64);
+ strcpy(exName, exBuildList[i]);
+ memset(exCategory, 0, 32);
+ strncpy(exCategory, exName, TextFindIndex(exName, "_"));
+
+ // Skip some examples from building
+ if ((strcmp(exName, "core_custom_logging") == 0) ||
+ (strcmp(exName, "core_window_should_close") == 0) ||
+ (strcmp(exName, "core_custom_frame_control") == 0)) continue;
+
+ LOG("INFO: [%i/%i] Checking example log: [%s]\n", i + 1, exBuildListCount, exName);
+
+ // Load .build.log to check for compilation warnings
+ char *exTestBuildLog = LoadFileText(TextFormat("%s/%s/logs/%s.build.log", exBasePath, exCategory, exName));
+ if (exTestBuildLog == NULL)
+ {
+ LOG("WARNING: [%s] Build log could not be loaded\n", exName);
+ continue;
+ }
+
+ // Load build log text lines
+ int exTestBuildLogLinesCount = 0;
+ char **exTestBuildLogLines = LoadTextLines(exTestBuildLog, &exTestBuildLogLinesCount);
+
+ for (int k = 0; k < exTestBuildLogLinesCount; k++)
+ {
+ // Checking compilation warnings generated
+ if (TextFindIndex(exTestBuildLogLines[k], "warning:") >= 0) testing[i].buildwarns++;
+ }
+
+ UnloadTextLines(exTestBuildLogLines, exTestBuildLogLinesCount);
+ UnloadFileText(exTestBuildLog);
+
+#if defined(BUILD_TESTING_WEB)
+ // TODO: REVIEW: Hardcoded path where web logs are copied after automatic download
+ char *exTestLog = LoadFileText(TextFormat("D:/testing_logs_web/%s.log", exName));
+#else
+ char *exTestLog = LoadFileText(TextFormat("%s/%s/logs/%s.log", exBasePath, exCategory, exName));
+#endif
+ if (exTestLog == NULL)
+ {
+ LOG("WARNING: [%s] Execution log could not be loaded\n", exName);
+ testing[i].status = 0b1111111;
+ continue;
+ }
+
+ /*
+ TESTING_FAIL_INIT = 1 << 0, // Initialization (InitWindow()) -> "INFO: DISPLAY: Device initialized successfully"
+ TESTING_FAIL_CLOSE = 1 << 1, // Closing (CloseWindow()) -> "INFO: Window closed successfully"
+ TESTING_FAIL_ASSETS = 1 << 2, // Assets loading (WARNING: FILE:) -> "WARNING: FILEIO:"
+ TESTING_FAIL_RLGL = 1 << 3, // OpenGL-wrapped initialization -> "INFO: RLGL: Default OpenGL state initialized successfully"
+ TESTING_FAIL_PLATFORM = 1 << 4, // Platform initialization -> "INFO: PLATFORM: DESKTOP (GLFW - Win32): Initialized successfully"
+ TESTING_FAIL_FONT = 1 << 5, // Font default initialization -> "INFO: FONT: Default font loaded successfully (224 glyphs)"
+ TESTING_FAIL_TIMER = 1 << 6, // Timer initialization -> "INFO: TIMER: Target time per frame: 16.667 milliseconds"
+ */
+
+ if (TextFindIndex(exTestLog, "INFO: DISPLAY: Device initialized successfully") == -1) testing[i].status |= TESTING_FAIL_INIT;
+ if (TextFindIndex(exTestLog, "INFO: Window closed successfully") == -1) testing[i].status |= TESTING_FAIL_CLOSE;
+ if (TextFindIndex(exTestLog, "WARNING: FILEIO:") >= 0) testing[i].status |= TESTING_FAIL_ASSETS;
+ if (TextFindIndex(exTestLog, "INFO: RLGL: Default OpenGL state initialized successfully") == -1) testing[i].status |= TESTING_FAIL_RLGL;
+ if (TextFindIndex(exTestLog, "INFO: PLATFORM:") == -1) testing[i].status |= TESTING_FAIL_PLATFORM;
+ if (TextFindIndex(exTestLog, "INFO: FONT: Default font loaded successfully") == -1) testing[i].status |= TESTING_FAIL_FONT;
+ if (TextFindIndex(exTestLog, "INFO: TIMER: Target time per frame:") == -1) testing[i].status |= TESTING_FAIL_TIMER;
+
+ // Load build log text lines
+ int exTestLogLinesCount = 0;
+ char **exTestLogLines = LoadTextLines(exTestLog, &exTestLogLinesCount);
+ for (int k = 0; k < exTestLogLinesCount; k++)
+ {
+#if defined(BUILD_TESTING_WEB)
+ if (TextFindIndex(exTestLogLines[k], "WARNING: GL: NPOT") >= 0) continue; // Ignore web-specific warning
+#endif
+#if defined(PLATFORM_DRM)
+ if (TextFindIndex(exTestLogLines[k], "WARNING: DISPLAY: No graphic") >= 0) continue; // Ignore specific warning
+ if (TextFindIndex(exTestLogLines[k], "WARNING: GetCurrentMonitor()") >= 0) continue; // Ignore specific warning
+ if (TextFindIndex(exTestLogLines[k], "WARNING: SetWindowPosition()") >= 0) continue; // Ignore specific warning
+#endif
+ if (TextFindIndex(exTestLogLines[k], "WARNING") >= 0) testing[i].warnings++;
+ }
+ UnloadTextLines(exTestLogLines, exTestLogLinesCount);
+ UnloadFileText(exTestLog);
+ }
+ //---------------------------------------------------------------------------------------------
+
+ // STEP 5: Generate testing report/table with results (.md)
+ //-----------------------------------------------------------------------------------------------------
+#if defined(BUILD_TESTING_WEB)
+ const char *osName = "Web";
+#else
+ #if defined(PLATFORM_DRM)
+ const char *osName = "DRM";
+ #elif defined(PLATFORM_DESKTOP)
+ #if defined(_WIN32)
+ const char *osName = "Windows";
+ #elif defined(__linux__)
+ const char *osName = "Linux";
+ #elif defined(__FreeBSD__)
+ const char *osName = "FreeBSD";
+ #elif defined(__APPLE__)
+ const char *osName = "macOS";
+ #endif // Desktop OSs
+ #endif
+#endif
+ /*
+ Columns:
+ - [CWARN] : Compilation WARNING messages
+ - [LWARN] : Log WARNING messages count
+ - [INIT] : Initialization
+ - [CLOSE] : Closing
+ - [ASSETS] : Assets loading
+ - [RLGL] : OpenGL-wrapped initialization
+ - [PLAT] : Platform initialization
+ - [FONT] : Font default initialization
+ - [TIMER] : Timer initialization
+
+ | **EXAMPLE NAME** | [CWARN] | [LWARN] | [INIT] | [CLOSE] | [ASSETS] | [RLGL] | [PLAT] | [FONT] | [TIMER] |
+ |:---------------------------------|:-------:|:-------:|:------:|:-------:|:--------:|:------:|:------:|:------:|:-------:|
+ | core_basic window | 0 | 0 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+ */
+ LOG("INFO: [examples_testing_os.md] Generating examples testing report...\n");
+
+ char *report = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1);
+
+ int repIndex = 0;
+ repIndex += sprintf(report + repIndex, "# EXAMPLES COLLECTION - TESTING REPORT\n\n");
+ repIndex += sprintf(report + repIndex, TextFormat("## Tested Platform: %s\n\n", osName));
+
+ repIndex += sprintf(report + repIndex, "```\nExample automated testing elements validated:\n");
+ repIndex += sprintf(report + repIndex, " - [CWARN] : Compilation WARNING messages\n");
+ repIndex += sprintf(report + repIndex, " - [LWARN] : Log WARNING messages count\n");
+ repIndex += sprintf(report + repIndex, " - [INIT] : Initialization\n");
+ repIndex += sprintf(report + repIndex, " - [CLOSE] : Closing\n");
+ repIndex += sprintf(report + repIndex, " - [ASSETS] : Assets loading\n");
+ repIndex += sprintf(report + repIndex, " - [RLGL] : OpenGL-wrapped initialization\n");
+ repIndex += sprintf(report + repIndex, " - [PLAT] : Platform initialization\n");
+ repIndex += sprintf(report + repIndex, " - [FONT] : Font default initialization\n");
+ repIndex += sprintf(report + repIndex, " - [TIMER] : Timer initialization\n```\n");
+
+ repIndex += sprintf(report + repIndex, "| **EXAMPLE NAME** | [CWARN] | [LWARN] | [INIT] | [CLOSE] | [ASSETS] | [RLGL] | [PLAT] | [FONT] | [TIMER] |\n");
+ repIndex += sprintf(report + repIndex, "|:---------------------------------|:-------:|:-------:|:------:|:-------:|:--------:|:------:|:------:|:------:|:-------:|\n");
+
+ /*
+ TESTING_FAIL_INIT = 1 << 0, // Initialization (InitWindow()) -> "INFO: DISPLAY: Device initialized successfully"
+ TESTING_FAIL_CLOSE = 1 << 1, // Closing (CloseWindow()) -> "INFO: Window closed successfully"
+ TESTING_FAIL_ASSETS = 1 << 2, // Assets loading (WARNING: FILE:) -> "WARNING: FILEIO:"
+ TESTING_FAIL_RLGL = 1 << 3, // OpenGL-wrapped initialization -> "INFO: RLGL: Default OpenGL state initialized successfully"
+ TESTING_FAIL_PLATFORM = 1 << 4, // Platform initialization -> "INFO: PLATFORM: DESKTOP (GLFW - Win32): Initialized successfully"
+ TESTING_FAIL_FONT = 1 << 5, // Font default initialization -> "INFO: FONT: Default font loaded successfully (224 glyphs)"
+ TESTING_FAIL_TIMER = 1 << 6, // Timer initialization -> "INFO: TIMER: Target time per frame: 16.667 milliseconds"
+ */
+ for (int i = 0; i < exBuildListCount; i++)
+ {
+ if ((testing[i].buildwarns > 0) || (testing[i].warnings > 0) || (testing[i].status > 0))
+ {
+ repIndex += sprintf(report + repIndex, "| %-32s | %i | %i | %s | %s | %s | %s | %s | %s | %s |\n",
+ exBuildList[i],
+ testing[i].buildwarns,
+ testing[i].warnings,
+ (testing[i].status & TESTING_FAIL_INIT)? "❌" : "✔",
+ (testing[i].status & TESTING_FAIL_CLOSE)? "❌" : "✔",
+ (testing[i].status & TESTING_FAIL_ASSETS)? "❌" : "✔",
+ (testing[i].status & TESTING_FAIL_RLGL)? "❌" : "✔",
+ (testing[i].status & TESTING_FAIL_PLATFORM)? "❌" : "✔",
+ (testing[i].status & TESTING_FAIL_FONT)? "❌" : "✔",
+ (testing[i].status & TESTING_FAIL_TIMER)? "❌" : "✔");
+ }
+ }
+
+ repIndex += sprintf(report + repIndex, "\n");
+
+ SaveFileText(TextFormat("%s/../tools/rexm/reports/examples_testing_%s.md", exBasePath, TextToLower(osName)), report);
+
+ RL_FREE(report);
+ //-----------------------------------------------------------------------------------------------------
+
} break;
default: // Help
{
@@ -1468,6 +1872,7 @@ int main(int argc, char *argv[])
printf(" rename : Rename an existing example\n");
printf(" remove : Remove an existing example\n");
printf(" build : Build example for Desktop and Web platforms\n");
+ printf(" test : Build and Test example for Desktop and Web platforms\n");
printf(" validate : Validate examples collection, generates report\n");
printf(" update : Validate and update examples collection, generates report\n\n");
printf("OPTIONS:\n\n");
@@ -1498,13 +1903,13 @@ static int UpdateRequiredFiles(void)
//------------------------------------------------------------------------------------------------
LOG("INFO: Updating all examples metadata...\n");
int exListCount = 0;
- rlExampleInfo *exList = LoadExamplesData(exCollectionFilePath, "ALL", true, &exListCount);
+ rlExampleInfo *exList = LoadExampleData("ALL", true, &exListCount);
for (int i = 0; i < exListCount; i++)
{
rlExampleInfo *info = &exList[i];
UpdateSourceMetadata(TextFormat("%s/%s/%s.c", exBasePath, info->category, info->name), info);
}
- UnloadExamplesData(exList);
+ UnloadExampleData(exList);
//------------------------------------------------------------------------------------------------
// Edit: raylib/examples/Makefile --> Update from collection
@@ -1525,12 +1930,12 @@ static int UpdateRequiredFiles(void)
mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i])));
int exCollectionCount = 0;
- rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], true, &exCollectionCount);
+ rlExampleInfo *exCollection = LoadExampleData(exCategories[i], true, &exCollectionCount);
for (int x = 0; x < exCollectionCount - 1; x++) mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat(" %s/%s \\\n", exCollection[x].category, exCollection[x].name));
mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat(" %s/%s\n\n", exCollection[exCollectionCount - 1].category, exCollection[exCollectionCount - 1].name));
- UnloadExamplesData(exCollection);
+ UnloadExampleData(exCollection);
}
// Add the remaining part of the original file
@@ -1562,12 +1967,12 @@ static int UpdateRequiredFiles(void)
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i])));
int exCollectionCount = 0;
- rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], true, &exCollectionCount);
+ rlExampleInfo *exCollection = LoadExampleData(exCategories[i], true, &exCollectionCount);
for (int x = 0; x < exCollectionCount - 1; x++) mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat(" %s/%s \\\n", exCollection[x].category, exCollection[x].name));
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat(" %s/%s\n\n", exCollection[exCollectionCount - 1].category, exCollection[exCollectionCount - 1].name));
- UnloadExamplesData(exCollection);
+ UnloadExampleData(exCollection);
}
// Add examples individual targets, considering every example resources
@@ -1588,13 +1993,13 @@ static int UpdateRequiredFiles(void)
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("# Compile %s examples\n", TextToUpper(exCategories[i])));
int exCollectionCount = 0;
- rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], true, &exCollectionCount);
+ rlExampleInfo *exCollection = LoadExampleData(exCategories[i], true, &exCollectionCount);
for (int x = 0; x < exCollectionCount; x++)
{
// Scan resources used in example to list
int resPathCount = 0;
- char **resPaths = ScanExampleResources(TextFormat("%s/%s/%s.c", exBasePath, exCollection[x].category, exCollection[x].name), &resPathCount);
+ char **resPaths = LoadExampleResourcePaths(TextFormat("%s/%s/%s.c", exBasePath, exCollection[x].category, exCollection[x].name), &resPathCount);
if (resPathCount > 0)
{
@@ -1647,10 +2052,10 @@ static int UpdateRequiredFiles(void)
mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, " $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)\n\n");
}
- ClearExampleResources(resPaths);
+ UnloadExampleResourcePaths(resPaths);
}
- UnloadExamplesData(exCollection);
+ UnloadExampleData(exCollection);
}
// Add the remaining part of the original file
@@ -1676,8 +2081,8 @@ static int UpdateRequiredFiles(void)
memcpy(mdTextUpdated, mdText, mdListStartIndex);
int exCollectionFullCount = 0;
- rlExampleInfo *exCollectionFull = LoadExamplesData(exCollectionFilePath, "ALL", false, &exCollectionFullCount);
- UnloadExamplesData(exCollectionFull);
+ rlExampleInfo *exCollectionFull = LoadExampleData("ALL", false, &exCollectionFullCount);
+ UnloadExampleData(exCollectionFull);
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("## EXAMPLES COLLECTION [TOTAL: %i]\n", exCollectionFullCount));
@@ -1685,14 +2090,14 @@ static int UpdateRequiredFiles(void)
for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++)
{
int exCollectionCount = 0;
- rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], false, &exCollectionCount);
+ rlExampleInfo *exCollection = LoadExampleData(exCategories[i], false, &exCollectionCount);
// Every category includes some introductory text, as it is quite short, just copying it here
if (i == 0) // "core"
{
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: core [%i]\n\n", exCollectionCount));
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
- "Examples using raylib[core](../src/rcore.c) platform functionality like window creation, inputs, drawing modes and system functionality.\n\n");
+ "Examples using raylib [core](../src/rcore.c) module platform functionality: window creation, inputs, drawing modes and system functionality.\n\n");
}
else if (i == 1) // "shapes"
{
@@ -1756,13 +2161,13 @@ static int UpdateRequiredFiles(void)
starsText, exCollection[x].verCreated, exCollection[x].verUpdated, exCollection[x].author, exCollection[x].authorGitHub));
}
- UnloadExamplesData(exCollection);
+ UnloadExampleData(exCollection);
}
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
"\nSome example missing? As always, contributions are welcome, feel free to send new examples!\n");
mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
- "Here is an[examples template](examples_template.c) with instructions to start with!\n");
+ "Here is an [examples template](examples_template.c) with instructions to start with!\n");
// Save updated file
SaveFileText(TextFormat("%s/README.md", exBasePath), mdTextUpdated);
@@ -1802,7 +2207,7 @@ static int UpdateRequiredFiles(void)
for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++)
{
int exCollectionCount = 0;
- rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], false, &exCollectionCount);
+ rlExampleInfo *exCollection = LoadExampleData(exCategories[i], false, &exCollectionCount);
for (int x = 0; x < exCollectionCount; x++)
{
for (int s = 0; s < 4; s++)
@@ -1824,7 +2229,7 @@ static int UpdateRequiredFiles(void)
}
}
- UnloadExamplesData(exCollection);
+ UnloadExampleData(exCollection);
}
// Add the remaining part of the original file
@@ -1841,8 +2246,8 @@ static int UpdateRequiredFiles(void)
return result;
}
-// Load examples collection information
-static rlExampleInfo *LoadExamplesData(const char *fileName, const char *category, bool sort, int *exCount)
+// Load examples information from collection data
+static rlExampleInfo *LoadExampleData(const char *filter, bool sort, int *exCount)
{
#define MAX_EXAMPLES_INFO 256
@@ -1850,7 +2255,8 @@ static rlExampleInfo *LoadExamplesData(const char *fileName, const char *categor
int exCounter = 0;
*exCount = 0;
- char *text = LoadFileText(fileName);
+ // Load main collection list file: "raylib/examples/examples_list.txt"
+ char *text = LoadFileText(exCollectionFilePath);
if (text != NULL)
{
@@ -1872,18 +2278,25 @@ static rlExampleInfo *LoadExamplesData(const char *fileName, const char *categor
int result = ParseExampleInfoLine(lines[i], &info);
if (result == 1) // Success on parsing
{
- if (strcmp(category, "ALL") == 0)
+ if (strcmp(filter, "ALL") == 0)
{
// Add all examples to the list
memcpy(&exInfo[exCounter], &info, sizeof(rlExampleInfo));
exCounter++;
}
- else if (strcmp(info.category, category) == 0)
+ else if (strcmp(info.category, filter) == 0)
{
// Get only specific category examples
memcpy(&exInfo[exCounter], &info, sizeof(rlExampleInfo));
exCounter++;
}
+ else if (strcmp(info.name, filter) == 0)
+ {
+ // Get only requested example
+ memcpy(&exInfo[exCounter], &info, sizeof(rlExampleInfo));
+ exCounter++;
+ break;
+ }
}
}
}
@@ -1900,7 +2313,7 @@ static rlExampleInfo *LoadExamplesData(const char *fileName, const char *categor
}
// Unload examples collection data
-static void UnloadExamplesData(rlExampleInfo *exInfo)
+static void UnloadExampleData(rlExampleInfo *exInfo)
{
RL_FREE(exInfo);
}
@@ -1909,11 +2322,14 @@ static void UnloadExamplesData(rlExampleInfo *exInfo)
// WARNING: Expecting the example to follow raylib_example_template.c
static rlExampleInfo *LoadExampleInfo(const char *exFileName)
{
- rlExampleInfo *exInfo = (rlExampleInfo *)RL_CALLOC(1, sizeof(rlExampleInfo));
+ rlExampleInfo *exInfo = NULL;
if (FileExists(exFileName) && IsFileExtension(exFileName, ".c"))
{
- strcpy(exInfo->name, GetFileNameWithoutExt(exFileName));
+ // Example found in collection
+ exInfo = (rlExampleInfo *)RL_CALLOC(1, sizeof(rlExampleInfo));
+
+ strncpy(exInfo->name, GetFileNameWithoutExt(exFileName), 128 - 1);
strncpy(exInfo->category, exInfo->name, TextFindIndex(exInfo->name, "_"));
char *exText = LoadFileText(exFileName);
@@ -1987,7 +2403,7 @@ static rlExampleInfo *LoadExampleInfo(const char *exFileName)
UnloadFileText(exText);
- exInfo->resPaths = ScanExampleResources(exFileName, &exInfo->resCount);
+ exInfo->resPaths = LoadExampleResourcePaths(exFileName, &exInfo->resCount);
}
return exInfo;
@@ -1996,7 +2412,7 @@ static rlExampleInfo *LoadExampleInfo(const char *exFileName)
// Unload example information
static void UnloadExampleInfo(rlExampleInfo *exInfo)
{
- ClearExampleResources(exInfo->resPaths);
+ UnloadExampleResourcePaths(exInfo->resPaths);
RL_FREE(exInfo);
}
@@ -2072,7 +2488,7 @@ static void SortExampleByName(rlExampleInfo *items, int count)
// but new examples could require other file extensions to be added,
// maybe it should look for '.xxx")' patterns instead
// TODO: WARNING: Some resources could require linked resources: .fnt --> .png, .mtl --> .png, .gltf --> .png, ...
-static char **ScanExampleResources(const char *filePath, int *resPathCount)
+static char **LoadExampleResourcePaths(const char *filePath, int *resPathCount)
{
#define REXM_MAX_RESOURCE_PATH_LEN 256
@@ -2096,16 +2512,19 @@ static char **ScanExampleResources(const char *filePath, int *resPathCount)
if (!end) break;
// WARNING: Some paths could be for saving files, not loading, those "resource" files must be omitted
- // HACK: Just check previous position from pointer for function name including the string and the index "distance"
+ // TODO: HACK: Just check previous position from pointer for function name including the string and the index "distance"
// This is a quick solution, the good one would be getting the data loading function names...
int functionIndex01 = TextFindIndex(ptr - 40, "ExportImage"); // Check ExportImage()
int functionIndex02 = TextFindIndex(ptr - 10, "TraceLog"); // Check TraceLog()
int functionIndex03 = TextFindIndex(ptr - 40, "TakeScreenshot"); // Check TakeScreenshot()
-
+ int functionIndex04 = TextFindIndex(ptr - 40, "SaveFileData"); // Check SaveFileData()
+ int functionIndex05 = TextFindIndex(ptr - 40, "SaveFileText"); // Check SaveFileText()
if (!((functionIndex01 != -1) && (functionIndex01 < 40)) && // Not found ExportImage() before ""
!((functionIndex02 != -1) && (functionIndex02 < 10)) && // Not found TraceLog() before ""
- !((functionIndex03 != -1) && (functionIndex03 < 40))) // Not found TakeScreenshot() before ""
+ !((functionIndex03 != -1) && (functionIndex03 < 40)) && // Not found TakeScreenshot() before ""
+ !((functionIndex04 != -1) && (functionIndex04 < 40)) && // Not found TakeScreenshot() before ""
+ !((functionIndex05 != -1) && (functionIndex05 < 40))) // Not found SaveFileText() before ""
{
int len = (int)(end - start);
if ((len > 0) && (len < REXM_MAX_RESOURCE_PATH_LEN))
@@ -2151,7 +2570,7 @@ static char **ScanExampleResources(const char *filePath, int *resPathCount)
}
// Clear resource paths scanned
-static void ClearExampleResources(char **resPaths)
+static void UnloadExampleResourcePaths(char **resPaths)
{
for (int i = 0; i < REXM_MAX_RESOURCE_PATHS; i++) RL_FREE(resPaths[i]);
@@ -2210,29 +2629,29 @@ static int AddVSProjectToSolution(const char *slnFile, const char *projFile, con
// Add project config lines
offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t{%s}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug.DLL|x64.Build.0 = Debug.DLL|x64\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|ARM64.ActiveCfg = Debug|ARM64\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|ARM64.Build.0 = Debug|ARM64\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|x64.ActiveCfg = Debug|x64\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|x64.Build.0 = Debug|x64\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|x86.ActiveCfg = Debug|Win32\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|x86.Build.0 = Debug|Win32\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|x64.ActiveCfg = Release.DLL|x64\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|x64.Build.0 = Release.DLL|x64\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|x86.Build.0 = Release.DLL|Win32\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|ARM64.ActiveCfg = Release|ARM64\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|ARM64.Build.0 = Release|ARM64\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|x64.ActiveCfg = Release|x64\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|x64.Build.0 = Release|x64\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|x86.ActiveCfg = Release|Win32\n", uuid));
- offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|x86.Build.0 = Release|Win32\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug.DLL|x64.Build.0 = Debug.DLL|x64\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|ARM64.ActiveCfg = Debug|ARM64\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|ARM64.Build.0 = Debug|ARM64\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|x64.ActiveCfg = Debug|x64\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|x64.Build.0 = Debug|x64\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|x86.ActiveCfg = Debug|Win32\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|x86.Build.0 = Debug|Win32\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|x64.ActiveCfg = Release.DLL|x64\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|x64.Build.0 = Release.DLL|x64\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|x86.Build.0 = Release.DLL|Win32\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|ARM64.ActiveCfg = Release|ARM64\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|ARM64.Build.0 = Release|ARM64\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|x64.ActiveCfg = Release|x64\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|x64.Build.0 = Release|x64\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|x86.ActiveCfg = Release|Win32\n", uuid));
+ offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|x86.Build.0 = Release|Win32\n", uuid));
// Write next section directly to avoid copy logic
offsetIndex += sprintf(slnTextUpdated + offsetIndex, "\tEndGlobalSection\n");
offsetIndex += sprintf(slnTextUpdated + offsetIndex, "\tGlobalSection(SolutionProperties) = preSolution\n");
@@ -2451,8 +2870,8 @@ static void UpdateWebMetadata(const char *exHtmlPath, const char *exFilePath)
char exTitle[64] = { 0 }; // Example title: fileName without extension, replacing underscores by spaces
// Get example name: replace underscore by spaces
- strcpy(exName, GetFileNameWithoutExt(exHtmlPathCopy));
- strcpy(exTitle, exName);
+ 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] = ' '; }
// Get example category from exName: copy until first underscore
@@ -2505,3 +2924,6 @@ static bool TextInList(const char *text, const char **list, int listCount)
return result;
}
+#if defined(__GNUC__) // GCC and Clang
+ #pragma GCC diagnostic pop
+#endif
diff --git a/tools/rexm/rexm.rc b/tools/rexm/rexm.rc
index ad125f796..4d3785a6a 100644
--- a/tools/rexm/rexm.rc
+++ b/tools/rexm/rexm.rc
@@ -6,8 +6,8 @@ PRODUCTVERSION 1,0,0,0
BEGIN
BLOCK "StringFileInfo"
BEGIN
- //BLOCK "080904E4" // English UK
- BLOCK "040904E4" // English US
+ //BLOCK "080904E4" // English UK
+ BLOCK "040904E4" // English US
BEGIN
VALUE "CompanyName", "Ramon Santamaria"
VALUE "FileDescription", "rexm | raylib examples manager"
@@ -21,7 +21,7 @@ BEGIN
END
BLOCK "VarFileInfo"
BEGIN
- //VALUE "Translation", 0x809, 1252 // English UK
- VALUE "Translation", 0x409, 1252 // English US
+ //VALUE "Translation", 0x809, 1252 // English UK
+ VALUE "Translation", 0x409, 1252 // English US
END
END
diff --git a/tools/rlparser/output/raylib_api.json b/tools/rlparser/output/raylib_api.json
index c0bc88681..66d8e9f30 100644
--- a/tools/rlparser/output/raylib_api.json
+++ b/tools/rlparser/output/raylib_api.json
@@ -40,7 +40,7 @@
"name": "RLAPI",
"type": "UNKNOWN",
"value": "__declspec(dllexport)",
- "description": "We are building the library as a Win32 shared library (.dll)"
+ "description": "Building the library as a Win32 shared library (.dll)"
},
{
"name": "PI",
@@ -769,22 +769,22 @@
{
"type": "Vector2",
"name": "offset",
- "description": "Camera offset (displacement from target)"
+ "description": "Camera offset (screen space offset from window origin)"
},
{
"type": "Vector2",
"name": "target",
- "description": "Camera target (rotation and zoom origin)"
+ "description": "Camera target (world space target point that is mapped to screen space offset)"
},
{
"type": "float",
"name": "rotation",
- "description": "Camera rotation in degrees"
+ "description": "Camera rotation in degrees (pivots around target)"
},
{
"type": "float",
"name": "zoom",
- "description": "Camera zoom (scaling), should be 1.0f by default"
+ "description": "Camera zoom (scaling around target), must not be set to 0, set to 1.0f for no scale"
}
]
},
@@ -4646,7 +4646,7 @@
"params": [
{
"type": "const char *",
- "name": "dir"
+ "name": "dirPath"
}
]
},
@@ -11730,7 +11730,7 @@
},
{
"name": "UpdateSound",
- "description": "Update sound buffer with new data (data and frame count should fit in sound)",
+ "description": "Update sound buffer with new data (default data format: 32 bit float, stereo)",
"returnType": "void",
"params": [
{
@@ -11897,7 +11897,7 @@
},
{
"name": "SetSoundPan",
- "description": "Set pan for a sound (0.5 is center)",
+ "description": "Set pan for a sound (-1.0 left, 0.0 center, 1.0 right)",
"returnType": "void",
"params": [
{
@@ -12150,7 +12150,7 @@
},
{
"name": "SetMusicPan",
- "description": "Set pan for a music (0.5 is center)",
+ "description": "Set pan for a music (-1.0 left, 0.0 center, 1.0 right)",
"returnType": "void",
"params": [
{
diff --git a/tools/rlparser/output/raylib_api.lua b/tools/rlparser/output/raylib_api.lua
index 5cb3c1d55..192ad963a 100644
--- a/tools/rlparser/output/raylib_api.lua
+++ b/tools/rlparser/output/raylib_api.lua
@@ -40,7 +40,7 @@ return {
name = "RLAPI",
type = "UNKNOWN",
value = "__declspec(dllexport)",
- description = "We are building the library as a Win32 shared library (.dll)"
+ description = "Building the library as a Win32 shared library (.dll)"
},
{
name = "PI",
@@ -769,22 +769,22 @@ return {
{
type = "Vector2",
name = "offset",
- description = "Camera offset (displacement from target)"
+ description = "Camera offset (screen space offset from window origin)"
},
{
type = "Vector2",
name = "target",
- description = "Camera target (rotation and zoom origin)"
+ description = "Camera target (world space target point that is mapped to screen space offset)"
},
{
type = "float",
name = "rotation",
- description = "Camera rotation in degrees"
+ description = "Camera rotation in degrees (pivots around target)"
},
{
type = "float",
name = "zoom",
- description = "Camera zoom (scaling), should be 1.0f by default"
+ description = "Camera zoom (scaling around target), must not be set to 0, set to 1.0f for no scale"
}
}
},
@@ -4167,7 +4167,7 @@ return {
description = "Change working directory, return true on success",
returnType = "bool",
params = {
- {type = "const char *", name = "dir"}
+ {type = "const char *", name = "dirPath"}
}
},
{
@@ -7995,7 +7995,7 @@ return {
},
{
name = "UpdateSound",
- description = "Update sound buffer with new data (data and frame count should fit in sound)",
+ description = "Update sound buffer with new data (default data format: 32 bit float, stereo)",
returnType = "void",
params = {
{type = "Sound", name = "sound"},
@@ -8105,7 +8105,7 @@ return {
},
{
name = "SetSoundPan",
- description = "Set pan for a sound (0.5 is center)",
+ description = "Set pan for a sound (-1.0 left, 0.0 center, 1.0 right)",
returnType = "void",
params = {
{type = "Sound", name = "sound"},
@@ -8268,7 +8268,7 @@ return {
},
{
name = "SetMusicPan",
- description = "Set pan for a music (0.5 is center)",
+ description = "Set pan for a music (-1.0 left, 0.0 center, 1.0 right)",
returnType = "void",
params = {
{type = "Music", name = "music"},
diff --git a/tools/rlparser/output/raylib_api.txt b/tools/rlparser/output/raylib_api.txt
index 9be8e517c..f60f8fc81 100644
--- a/tools/rlparser/output/raylib_api.txt
+++ b/tools/rlparser/output/raylib_api.txt
@@ -35,7 +35,7 @@ Define 007: RLAPI
Name: RLAPI
Type: UNKNOWN
Value: __declspec(dllexport)
- Description: We are building the library as a Win32 shared library (.dll)
+ Description: Building the library as a Win32 shared library (.dll)
Define 008: PI
Name: PI
Type: FLOAT
@@ -399,10 +399,10 @@ Struct 13: Camera3D (5 fields)
Struct 14: Camera2D (4 fields)
Name: Camera2D
Description: Camera2D, defines position/orientation in 2d space
- Field[1]: Vector2 offset // Camera offset (displacement from target)
- Field[2]: Vector2 target // Camera target (rotation and zoom origin)
- Field[3]: float rotation // Camera rotation in degrees
- Field[4]: float zoom // Camera zoom (scaling), should be 1.0f by default
+ Field[1]: Vector2 offset // Camera offset (screen space offset from window origin)
+ Field[2]: Vector2 target // Camera target (world space target point that is mapped to screen space offset)
+ Field[3]: float rotation // Camera rotation in degrees (pivots around target)
+ Field[4]: float zoom // Camera zoom (scaling around target), must not be set to 0, set to 1.0f for no scale
Struct 15: Mesh (17 fields)
Name: Mesh
Description: Mesh, vertex data and vao/vbo
@@ -1763,7 +1763,7 @@ Function 143: ChangeDirectory() (1 input parameters)
Name: ChangeDirectory
Return type: bool
Description: Change working directory, return true on success
- Param[1]: dir (type: const char *)
+ Param[1]: dirPath (type: const char *)
Function 144: IsPathFile() (1 input parameters)
Name: IsPathFile
Return type: bool
@@ -4483,7 +4483,7 @@ Function 543: IsSoundValid() (1 input parameters)
Function 544: UpdateSound() (3 input parameters)
Name: UpdateSound
Return type: void
- Description: Update sound buffer with new data (data and frame count should fit in sound)
+ Description: Update sound buffer with new data (default data format: 32 bit float, stereo)
Param[1]: sound (type: Sound)
Param[2]: data (type: const void *)
Param[3]: sampleCount (type: int)
@@ -4554,7 +4554,7 @@ Function 556: SetSoundPitch() (2 input parameters)
Function 557: SetSoundPan() (2 input parameters)
Name: SetSoundPan
Return type: void
- Description: Set pan for a sound (0.5 is center)
+ Description: Set pan for a sound (-1.0 left, 0.0 center, 1.0 right)
Param[1]: sound (type: Sound)
Param[2]: pan (type: float)
Function 558: WaveCopy() (1 input parameters)
@@ -4660,7 +4660,7 @@ Function 575: SetMusicPitch() (2 input parameters)
Function 576: SetMusicPan() (2 input parameters)
Name: SetMusicPan
Return type: void
- Description: Set pan for a music (0.5 is center)
+ Description: Set pan for a music (-1.0 left, 0.0 center, 1.0 right)
Param[1]: music (type: Music)
Param[2]: pan (type: float)
Function 577: GetMusicTimeLength() (1 input parameters)
diff --git a/tools/rlparser/output/raylib_api.xml b/tools/rlparser/output/raylib_api.xml
index 96dbdbf64..1bbeb175c 100644
--- a/tools/rlparser/output/raylib_api.xml
+++ b/tools/rlparser/output/raylib_api.xml
@@ -7,7 +7,7 @@
-
+
@@ -155,10 +155,10 @@
-
-
-
-
+
+
+
+
@@ -1111,7 +1111,7 @@
-
+
@@ -2998,7 +2998,7 @@
-
+
@@ -3043,7 +3043,7 @@
-
+
@@ -3111,7 +3111,7 @@
-
+
diff --git a/tools/rlparser/rlparser b/tools/rlparser/rlparser
deleted file mode 100755
index b68032c02..000000000
Binary files a/tools/rlparser/rlparser and /dev/null differ