From a8ec8472ff29b228f6c51b4156d140d4783fd33a Mon Sep 17 00:00:00 2001 From: Rico P Date: Fri, 2 Aug 2024 21:04:33 +0200 Subject: [PATCH 01/47] replace unicode characters by ascii characters in jar_xml to avoid warning in MSVC (#4196) --- src/external/jar_xm.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/external/jar_xm.h b/src/external/jar_xm.h index 4a1bfbf676f1..b5e80e48ab18 100644 --- a/src/external/jar_xm.h +++ b/src/external/jar_xm.h @@ -1204,7 +1204,7 @@ static void jar_xm_tone_portamento(jar_xm_context_t* ctx, jar_xm_channel_context } static void jar_xm_pitch_slide(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, float period_offset) { - /* Don't ask about the 4.f coefficient. I found mention of it nowhere. Found by ear™. */ + /* Don't ask about the 4.f coefficient. I found mention of it nowhere. Found by ear. */ if(ctx->module.frequency_type == jar_xm_LINEAR_FREQUENCIES) {period_offset *= 4.f; } ch->period += period_offset; jar_xm_CLAMP_DOWN(ch->period); @@ -1507,7 +1507,7 @@ static void jar_xm_handle_note_and_instrument(jar_xm_context_t* ctx, jar_xm_chan jar_xm_volume_slide(ch, ch->fine_volume_slide_param); break; case 0xD: /* EDy: Note delay */ - /* XXX: figure this out better. EDx triggers the note even when there no note and no instrument. But ED0 acts like like a ghost note, EDx (x ≠ 0) does not. */ + /* XXX: figure this out better. EDx triggers the note even when there no note and no instrument. But ED0 acts like like a ghost note, EDx (x != 0) does not. */ if(s->note == 0 && s->instrument == 0) { unsigned int flags = jar_xm_TRIGGER_KEEP_VOLUME; if(ch->current->effect_param & 0x0F) { @@ -1795,7 +1795,7 @@ static void jar_xm_tick(jar_xm_context_t* ctx) { if(ch->current->effect_param > 0) { char arp_offset = ctx->tempo % 3; switch(arp_offset) { - case 2: /* 0 -> x -> 0 -> y -> x -> … */ + case 2: /* 0 -> x -> 0 -> y -> x -> ... */ if(ctx->current_tick == 1) { ch->arp_in_progress = true; ch->arp_note_offset = ch->current->effect_param >> 4; @@ -1803,7 +1803,7 @@ static void jar_xm_tick(jar_xm_context_t* ctx) { break; } /* No break here, this is intended */ - case 1: /* 0 -> 0 -> y -> x -> … */ + case 1: /* 0 -> 0 -> y -> x -> ... */ if(ctx->current_tick == 0) { ch->arp_in_progress = false; ch->arp_note_offset = 0; @@ -1811,7 +1811,7 @@ static void jar_xm_tick(jar_xm_context_t* ctx) { break; } /* No break here, this is intended */ - case 0: /* 0 -> y -> x -> … */ + case 0: /* 0 -> y -> x -> ... */ jar_xm_arpeggio(ctx, ch, ch->current->effect_param, ctx->current_tick - arp_offset); default: break; From 6595bab8ff57dd2b8f7bd5b97bf4db1e6c28917f Mon Sep 17 00:00:00 2001 From: Anthony Carbajal <5776225+CrackedPixel@users.noreply.github.com> Date: Sun, 4 Aug 2024 14:45:50 -0500 Subject: [PATCH 02/47] update make for examples (#4209) --- examples/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/Makefile b/examples/Makefile index be8d872045e4..39484f791a51 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -479,6 +479,7 @@ CORE = \ core/core_automation_events \ core/core_basic_screen_manager \ core/core_basic_window \ + core/core_basic_window_web \ core/core_custom_frame_control \ core/core_custom_logging \ core/core_drop_files \ @@ -528,6 +529,7 @@ TEXTURES = \ textures/textures_draw_tiled \ textures/textures_fog_of_war \ textures/textures_gif_player \ + textures/textures_image_channel \ textures/textures_image_drawing \ textures/textures_image_generation \ textures/textures_image_kernel \ @@ -566,6 +568,7 @@ TEXT = \ MODELS = \ models/models_animation \ models/models_billboard \ + models/models_bone_socket \ models/models_box_collisions \ models/models_cubicmap \ models/models_draw_cube_texture \ @@ -586,6 +589,7 @@ MODELS = \ SHADERS = \ shaders/shaders_basic_lighting \ + shaders/shaders_basic_pbr \ shaders/shaders_custom_uniform \ shaders/shaders_deferred_render \ shaders/shaders_eratosthenes \ From 92f60a99f67bc1c4c502e7e80790a94e6d84f91d Mon Sep 17 00:00:00 2001 From: Randy Palamar <55642015+rnpnr@users.noreply.github.com> Date: Sun, 4 Aug 2024 13:53:56 -0600 Subject: [PATCH 03/47] [rlgl] use GLint64 for glGetBufferParameteri64v (#4197) --- src/rlgl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index 38f9ae8bd7f2..64c7a1ab6394 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -4397,7 +4397,7 @@ void rlUpdateShaderBuffer(unsigned int id, const void *data, unsigned int dataSi // Get SSBO buffer size unsigned int rlGetShaderBufferSize(unsigned int id) { - long long size = 0; + GLint64 size = 0; #if defined(GRAPHICS_API_OPENGL_43) glBindBuffer(GL_SHADER_STORAGE_BUFFER, id); From 43b0c9410eb3c52153e3c9b94132da3ea21ad12b Mon Sep 17 00:00:00 2001 From: Alex ZH Date: Sun, 4 Aug 2024 15:58:26 -0400 Subject: [PATCH 04/47] [examples] Add new example: `shaders_vertex_displacement` (#4186) * shaders-vertex_displacement init * implement simulation of wave in ocean * update examples/README & add some comments * update comments * add gl100 shaders --- examples/Makefile | 3 +- examples/Makefile.Web | 3 +- examples/README.md | 19 +-- .../shaders/glsl100/vertex_displacement.fs | 18 +++ .../shaders/glsl100/vertex_displacement.vs | 45 +++++++ .../shaders/glsl330/vertex_displacement.fs | 16 +++ .../shaders/glsl330/vertex_displacement.vs | 46 +++++++ .../shaders/shaders_vertex_displacement.c | 122 ++++++++++++++++++ .../shaders/shaders_vertex_displacement.png | Bin 0 -> 309007 bytes 9 files changed, 261 insertions(+), 11 deletions(-) create mode 100644 examples/shaders/resources/shaders/glsl100/vertex_displacement.fs create mode 100644 examples/shaders/resources/shaders/glsl100/vertex_displacement.vs create mode 100644 examples/shaders/resources/shaders/glsl330/vertex_displacement.fs create mode 100644 examples/shaders/resources/shaders/glsl330/vertex_displacement.vs create mode 100644 examples/shaders/shaders_vertex_displacement.c create mode 100644 examples/shaders/shaders_vertex_displacement.png diff --git a/examples/Makefile b/examples/Makefile index 39484f791a51..ee540606dab1 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -612,7 +612,8 @@ SHADERS = \ shaders/shaders_texture_outline \ shaders/shaders_texture_tiling \ shaders/shaders_texture_waves \ - shaders/shaders_write_depth + shaders/shaders_write_depth \ + shaders/shaders_vertex_displacement AUDIO = \ audio/audio_mixed_processor \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index af3325aa5650..46a8a98ea077 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -476,7 +476,8 @@ SHADERS = \ shaders/shaders_texture_outline \ shaders/shaders_texture_tiling \ shaders/shaders_texture_waves \ - shaders/shaders_write_depth + shaders/shaders_write_depth \ + shaders/shaders_vertex_displacement AUDIO = \ audio/audio_mixed_processor \ diff --git a/examples/README.md b/examples/README.md index d790f7d84b4a..317f43c899a3 100644 --- a/examples/README.md +++ b/examples/README.md @@ -178,6 +178,7 @@ Examples using raylib shaders functionality, including shaders loading, paramete | 115 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | shaders_multi_sample2d | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | | 116 | [shaders_spotlight](shaders/shaders_spotlight.c) | shaders_spotlight | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | | 117 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | shaders_deferred_render | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) | +| 118 | [shaders_vertex_displacement](shaders/shaders_vertex_displacement.c) | shaders_deferred_render | ⭐️☆☆☆ | 1.5 | 1.5 | [Alex ZH](https://github.com/ZzzhHe) | ### category: audio @@ -185,10 +186,10 @@ Examples using raylib audio functionality, including sound/music loading and pla | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| -| 118 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 119 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | -| 120 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) | -| 121 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | +| 119 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 120 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | +| 121 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) | +| 122 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | ### category: others @@ -196,11 +197,11 @@ Examples showing raylib misc functionality that does not fit in other categories | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| -| 122 | [rlgl_standalone](others/rlgl_standalone.c) | rlgl_standalone | ⭐️⭐️⭐️⭐️ | 1.6 | **4.0** | [Ray](https://github.com/raysan5) | -| 123 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | rlgl_compute_shader | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Teddy Astie](https://github.com/tsnake41) | -| 124 | [easings_testbed](others/easings_testbed.c) | easings_testbed | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) | -| 125 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | raylib_opengl_interop | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Stephan Soller](https://github.com/arkanis) | -| 126 | [embedded_files_loading](others/embedded_files_loading.c) | embedded_files_loading | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Kristian Holmgren](https://github.com/defutura) | +| 123 | [rlgl_standalone](others/rlgl_standalone.c) | rlgl_standalone | ⭐️⭐️⭐️⭐️ | 1.6 | **4.0** | [Ray](https://github.com/raysan5) | +| 124 | [rlgl_compute_shader](others/rlgl_compute_shader.c) | rlgl_compute_shader | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Teddy Astie](https://github.com/tsnake41) | +| 125 | [easings_testbed](others/easings_testbed.c) | easings_testbed | ⭐️⭐️⭐️☆ | 3.0 | 3.0 | [Juan Miguel López](https://github.com/flashback-fx) | +| 126 | [raylib_opengl_interop](others/raylib_opengl_interop.c) | raylib_opengl_interop | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Stephan Soller](https://github.com/arkanis) | +| 127 | [embedded_files_loading](others/embedded_files_loading.c) | embedded_files_loading | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Kristian Holmgren](https://github.com/defutura) | As always contributions are welcome, feel free to send new examples! Here is an [examples template](examples_template.c) to start with! diff --git a/examples/shaders/resources/shaders/glsl100/vertex_displacement.fs b/examples/shaders/resources/shaders/glsl100/vertex_displacement.fs new file mode 100644 index 000000000000..3328a91e7569 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl100/vertex_displacement.fs @@ -0,0 +1,18 @@ +#version 100 + +precision mediump float; + +// Input vertex attributes (from fragment shader) +varying vec2 fragTexCoord; +varying float height; + + +void main() +{ + vec4 darkblue = vec4(0.0, 0.13, 0.18, 1.0); + vec4 lightblue = vec4(1.0, 1.0, 1.0, 1.0); + // Interpolate between two colors based on height + vec4 finalColor = mix(darkblue, lightblue, height); + + gl_FragColor = finalColor; +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl100/vertex_displacement.vs b/examples/shaders/resources/shaders/glsl100/vertex_displacement.vs new file mode 100644 index 000000000000..c7b926d4b5f3 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl100/vertex_displacement.vs @@ -0,0 +1,45 @@ +#version 100 + +precision mediump float; + +attribute vec3 vertexPosition; +attribute vec2 vertexTexCoord; +attribute vec3 vertexNormal; +attribute vec4 vertexColor; + +uniform mat4 mvp; +uniform mat4 matModel; +uniform mat4 matNormal; + +uniform float time; + +uniform sampler2D perlinNoiseMap; + +varying vec3 fragPosition; +varying vec2 fragTexCoord; +varying vec3 fragNormal; +varying float height; + +void main() +{ + // Calculate animated texture coordinates based on time and vertex position + vec2 animatedTexCoord = sin(vertexTexCoord + vec2(sin(time + vertexPosition.x * 0.1), cos(time + vertexPosition.z * 0.1)) * 0.3); + + // Normalize animated texture coordinates to range [0, 1] + animatedTexCoord = animatedTexCoord * 0.5 + 0.5; + + // Fetch displacement from the perlin noise map + float displacement = texture2D(perlinNoiseMap, animatedTexCoord).r * 7.0; // Amplified displacement + + // Displace vertex position + vec3 displacedPosition = vertexPosition + vec3(0.0, displacement, 0.0); + + // Send vertex attributes to fragment shader + fragPosition = vec3(matModel * vec4(displacedPosition, 1.0)); + fragTexCoord = vertexTexCoord; + fragNormal = normalize(vec3(matNormal * vec4(vertexNormal, 1.0))); + height = displacedPosition.y * 0.2; // send height to fragment shader for coloring + + // Calculate final vertex position + gl_Position = mvp * vec4(displacedPosition, 1.0); +} diff --git a/examples/shaders/resources/shaders/glsl330/vertex_displacement.fs b/examples/shaders/resources/shaders/glsl330/vertex_displacement.fs new file mode 100644 index 000000000000..424f526e4d04 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/vertex_displacement.fs @@ -0,0 +1,16 @@ +#version 330 + +// Input fragment attributes (from fragment shader) +in vec2 fragTexCoord; +in float height; + +// Output fragment color +out vec4 finalColor; + +void main() +{ + vec4 darkblue = vec4(0.0, 0.13, 0.18, 1.0); + vec4 lightblue = vec4(1.0, 1.0, 1.0, 1.0); + // interplate between two colors based on height + finalColor = mix(darkblue, lightblue, height); +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl330/vertex_displacement.vs b/examples/shaders/resources/shaders/glsl330/vertex_displacement.vs new file mode 100644 index 000000000000..73cf16109ca2 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/vertex_displacement.vs @@ -0,0 +1,46 @@ +#version 330 + +// Input vertex attributes +in vec3 vertexPosition; +in vec2 vertexTexCoord; +in vec3 vertexNormal; +in vec4 vertexColor; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matModel; +uniform mat4 matNormal; + +uniform float time; + +uniform sampler2D perlinNoiseMap; + +// Output vertex attributes (to fragment shader) +out vec3 fragPosition; +out vec2 fragTexCoord; +out vec3 fragNormal; +out float height; + +void main() +{ + // Calculate animated texture coordinates based on time and vertex position + vec2 animatedTexCoord = sin(vertexTexCoord + vec2(sin(time + vertexPosition.x * 0.1), cos(time + vertexPosition.z * 0.1)) * 0.3); + + // Normalize animated texture coordinates to range [0, 1] + animatedTexCoord = animatedTexCoord * 0.5 + 0.5; + + // Fetch displacement from the perlin noise map + float displacement = texture(perlinNoiseMap, animatedTexCoord).r * 7; // Amplified displacement + + // Displace vertex position + vec3 displacedPosition = vertexPosition + vec3(0.0, displacement, 0.0); + + // Send vertex attributes to fragment shader + fragPosition = vec3(matModel*vec4(displacedPosition, 1.0)); + fragTexCoord = vertexTexCoord; + fragNormal = normalize(vec3(matNormal*vec4(vertexNormal, 1.0))); + height = displacedPosition.y * 0.2; // send height to fragment shader for coloring + + // Calculate final vertex position + gl_Position = mvp*vec4(displacedPosition , 1.0); +} diff --git a/examples/shaders/shaders_vertex_displacement.c b/examples/shaders/shaders_vertex_displacement.c new file mode 100644 index 000000000000..edce07ce7c0e --- /dev/null +++ b/examples/shaders/shaders_vertex_displacement.c @@ -0,0 +1,122 @@ +/******************************************************************************************* +* +* raylib [shaders] example - Vertex displacement +* +* Example originally created with raylib 5.0, last time updated with raylib 4.5 +* +* Example contributed by (@ZzzhHe) 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) 2023 (@ZzzhHe) +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" +#include "rlgl.h" + +#include + +#define RLIGHTS_IMPLEMENTATION +#include "rlights.h" + +#if defined(PLATFORM_DESKTOP) + #define GLSL_VERSION 330 +#else // PLATFORM_ANDROID, PLATFORM_WEB + #define GLSL_VERSION 100 +#endif + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - vertex displacement"); + + // set up camera + Camera camera = {0}; + camera.position = (Vector3) {20.0f, 5.0f, -20.0f}; + camera.target = (Vector3) {0.0f, 0.0f, 0.0f}; + camera.up = (Vector3) {0.0f, 1.0f, 0.0f}; + camera.fovy = 60.0f; + camera.projection = CAMERA_PERSPECTIVE; + + // Load vertex and fragment shaders + Shader shader = LoadShader( + TextFormat("resources/shaders/glsl%i/vertex_displacement.vs", GLSL_VERSION), + TextFormat("resources/shaders/glsl%i/vertex_displacement.fs", GLSL_VERSION) + ); + + // Load perlin noise texture + Image perlinNoiseImage = GenImagePerlinNoise(512, 512, 0, 0, 1.0f); + Texture perlinNoiseMap = LoadTextureFromImage(perlinNoiseImage); + UnloadImage(perlinNoiseImage); + + // Set shader uniform location + int perlinNoiseMapLoc = GetShaderLocation(shader, "perlinNoiseMap"); + rlEnableShader(shader.id); + rlActiveTextureSlot(1); + rlEnableTexture(perlinNoiseMap.id); + rlSetUniformSampler(perlinNoiseMapLoc, 1); + + // Create a plane mesh and model + Mesh planeMesh = GenMeshPlane(50, 50, 50, 50); + Model planeModel = LoadModelFromMesh(planeMesh); + // Set plane model material + planeModel.materials[0].shader = shader; + + float time = 0.0f; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera, CAMERA_FREE); // Update camera + + time += GetFrameTime(); // Update time variable + SetShaderValue(shader, GetShaderLocation(shader, "time"), &time, SHADER_UNIFORM_FLOAT); // Send time value to shader + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + BeginShaderMode(shader); + // Draw plane model + DrawModel(planeModel, (Vector3){ 0.0f, 0.0f, 0.0f }, 1.0f, (Color) {255, 255, 255, 255}); + EndShaderMode(); + + EndMode3D(); + + DrawText("Vertex displacement", 10, 10, 20, DARKGRAY); + DrawFPS(10, 40); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + + UnloadShader(shader); + UnloadModel(planeModel); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/shaders/shaders_vertex_displacement.png b/examples/shaders/shaders_vertex_displacement.png new file mode 100644 index 0000000000000000000000000000000000000000..e7acf5c651a8961d3a6982db2087069105572c8e GIT binary patch literal 309007 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

1mP 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%0D0( zHUh@N$*9u4`D8vme=~#vRFz(oI)k*J6@db=0>`4RAA$&W?vyAghvgoFsrca)3UR zkVm^P;7RH^+Buf()d~6uirIOvQAugTa2ty<`__j)d%Vkb)};oSCRqln0`Na8ExY zBa=v!7!DETUJO>8-^7CR0tK2v-a)_^f31ufX$|%E()Djz`SilKi*P@s0n$M#jxd2^ zWv$Ux^%%U?jv$hvU8mJ&lhZ>4=-C4AbNDrPv#JHiO$h=gzElHL2tzh#y_((-9heNY zw;qlfuq-T@!vHLMEec^x`TD)?nG`8m^d{JeKl`Php$|9cLl4|Q{0B$+A8jo}bYwr; zy={&>G*Lbu)x@RD@jP5rc~i*1Cx(;~M?8{Vw;Ym5N`hOQ7GOzN>>#`rE)9F3BiC9P z`;1XpzG?3a*0zgEh12YuB&g1ZdLmd*8C35c9qGc1kYX#_7PDE%Zc_z=DLgdogpN$c zA1+oerS+uenaN`zTfj-$lUWrXpSxy4+{YpdCjN}k_kviu=#l9m3t_a6@d-+#FJroX zS7!902&MTd&)L%YwgS!q4cgv{8$E{uj*sk7Y6u=UCtn+J;)#zRgy7!@d4gY!^)dcx ztp3%WJVwt>zl1!{>Qdk5Bv0FXb3ZzhuI99WUOpZ52afxuwuvl=ql}=WQ%X;U<4@3^BDhWqzqduYZ?m{18+jCFbSIPK*a!}sR#>JBkffm9O{*jWE!)uC2 z-p1*%FWWA4PLs%iQFBlR%{iaZ{3>X76br68_;F1Tfy}{pg1WyNdvz< zb6?#qkBeqt0pU42@BuRO&#Q=ObPL8tR>COpRFeVHQaGMt1%Zbbq{mcY1Y!ui=Qg^o*xKR#^1p!wyFaueXecm_ zHiL+sM}QwZdBR6Wyv1{?M?~mcqZB?ZVSDzO+!7`U>7{+DBSv4)nLdf})JDajKtuzH-Y4bS9#2we+_-G9XF07`B6u8kbtYO|A!; z66DAgpQ)6`)b~x>j5ItwRN&i@b0=8$tJ>MR7Ynp{6>7fFX*nL3Ud3a{8_hC1XD*04 zyy6gBiWwUn*8l)O07*naRN)M|$x#H_*&Dmh6|~gZ?0p34qbMcg&7Q&4{@QJ_TD<1j zH<*kZ3tE}PYc&d6bF}YqJ#W*71(WIzxm7I~NX~lVUuy;XBEQOtb_gHKVKErf9GJ`5 zqueZ|ZaHg&N~rBg+b1ZCSttPrgY&=O+QYOwN*GBv$_%ieo1H-R>QW7|)vA2?Kar8uR ztJ)TqmM03c@r<6Z`#>nuB{h$Bo|*Iu*#b_|o-9-VFDl)Fkj$nmLknGp+vCDTpG*%~ z2zysX_7th^NoJjBbWw*ZTZ;N0m8-}!MzvsnA60`pj&0uuFM6DZA#KhO868H}sqs0% zC!c%*-=^UwLO%QKGkZZ~fOxCd^F-rYzAmHx6f*s@T+P=lUO3fFP1$;2? zNmZeh^q|L4xx1Cu*GC3pJv|xjEk=!kPKU@7Ew|%H*(_q@H`iwbWB`7AxvkH*2D?LA zaM3)thVY`vmVTG*;+)>>X+jImTyER-r6Sm}v-_v|kmM!qb*Z{esLNbtbgHHMFFFXR zDFASNh&*($MyZD8_{E@=EZ}Nm9MxSw1_RxV?}|3gx0T8?VgiSaUDB-X{b%*btR-+| z3`PVE1*3+VVd%|5>ym5jDx{}6@tFs{_l4G|qn|3t2Ii}qm&#UU)N2rVLeFzTYN{0A zrYkANLaHBv5KMeoBgD4a7$dRdyp<#l=c4A>k8PNvb4sQSkfJ4zfaE-)$PE|fTIPjQ z9}6w6UaRYv_XFjk0Ajo*+?ea;Yk@PaL?mAyL;5^zPgQ3ZI*>f%sI<$6lbfuGTl`==Q@s`(o^1h`cNGpYNY+oJ*xR^ zro+o6B+i&4n1eqI!GZQVs%|OU;@tnJ>ur{y*ENG{?9k=|3Rjm_AMrdhX*aS>DOu!X zR?%a4CLUzjhq4SE=)K$?|1bJvddNcP-EN3(^&wpl%LVU9xd%ugeo*3Em^;xqNze^T zJ0of5r-*0B2S23GKVI{%4akZ~#f~zS*m2Gsy|cMy;h2E*r|Gqzw*^gM&ar<-qHxymq9@ zx_o3@>g7z|=<70x94P%f?G!*dXo5`%biLXUvpOA&cb7vod<=4%aP2IMnLc27#5&iF z7nNbZC+6L=kpa8h?NtX9!+YIHZ^KzoyIF1w3$)aLes^dT2Rsr#0m?nx?qggIDhNIE z2*HqnIcNP65PZEHRWc)+66TOZ4wM;?4#jPq+dSWGXfESuuToL?;22f&14v}x;`=!` zXmL;`|H0JymYU;e%$Z@@i-5VNA0iMHDDhg=R=5-Nb0f4R!k`D(+DUTqK}XNYf0(a2 zFb{9FEB7`pdJB(dz~}oozaN2B=b~AB4qIQup&PH2dAe@x8FONoodq zG%^R44pf4KVAqrJ7W+wiZKQJn^f)t3-lD_F&-EsjdgbA-zkw6pc+XaC7A^~E!`+mU zr>-D@;nghp5UHQZq_L4806zQ9?y8IXG<_)Y#lQF$=x=z?haDiIBZ#OK9wERCaRfO= zcg?Mn2Bfis0#v~l-Q-Hg*tf7CwFURm31r+Q139oX`U!Y&W}@Qr9gmx!*V?=^JFOPX zJVbFHl3Pkv z>IpwDTruNJ+F`}j`ujdkGtV=fXEyypx`3NJ0}BjX;m#4lXtGmBUXDSv5iVL~iSRY&< z^volYq?8l7Ex4r?&=@jHYwkseTq2PJp{5cjcA6I5ZOaMtXAOCX8}FhrnJi>6r&l5J zWWnCUOLFtXIvNj?xYveZZ44j#FIcMD%XwkTZ)9Q6BBXD48Fx#z=dh?Z`J@Nhd{g`S>^t7s5@SbF7WQ9VI#cei!O4CHJxE_wr*`6pDyRnHR|ZlO(7D93a9B05admW9N+Sx~K(g-Xr18PO=uAXEu+8bOASc2G*+nIGvR}%U+gQ&=otx?)yr*=#%Lo3!z$1 z$30D|d(ve4tk~i8ZS!(qsZNy)Qu3JpU>7Xk!ruqMzY&7HU%$n_nx6aUD2K-zWrx1^ z5^%1zvGKEDO|`s*TbB=jyzT;($j=hT{l^x`w!z zgRebhUnT1WWdR>dQ_B}z2I>IS21D*DiaZ^-w?K=h-goxN@{on_%(QDdR2Ibr?Ctbb zts^U|z<`j97yFf&n=DKh!i~U6>${m$)`WBGvrZbofEQ;}WjZ#G1{i%^G0J^GXux3r z?r?aZe$I8<45<6^Q(n;ThVMhU$ z*DBOq;}JtgE=Uxy&ddC~N=@V6iGuY zL_VBW4Mw*>bOm(b$uWFeW&xpiBPXz)$KLo1qP^G+XVCjhR>OU6Xvh%UmRd=9=nm!< zk{!`#Vj>+eWh@fNIzbZNHs+2}ci*R{Kl_tE`Scq8J}G|K@mfCccm*5q(W_Uq5BNy5 z{?LQ>3OV!-b+%HEq-+)2M3w~{L(v6!A+(fN47czPWt=zK;{j>3xHqJJN7sBJzvm?F zUrKHttly__*Whe{Kb`8u`Hoi2;r3{gTs)a3I#*qHbUjDsnN_I|l@-6; zaiIiwQK_v%o)wg3C~%L$_9$?9pxioSA>2)|r%82B+S%t?yX+c}`Olf9HKpYEnrLG; zfPY8;ewF6iw{PJ8Z=YV{@6}v=b@>7FykIu6Q7o8w?*p}wEI2+PIwwe2a z*!QPuqd4GEnEC)bD6`SY>t;{eS8d`AZBsJ&rGb%LOiNmPucq0}pY&ew2~Vf9-4&B1 zw_6I*3ri0z97}+4KsqRMt6?Kg=}=8XW}nvR0!^>Whz@*DO82LfM3Xs=ZkZvKH6ha_ zTeruGn|Ugj2<()+S9G>K;nufR1GzH0ZW2ivdoc1wDu9S1h%v18a|i25xav zwCqcTWi|EMvaD!9(qgi%G-?M(%nr>5fOIVcwO>z7#q%VX6S_5AoCSH=Z7Yjhiy9Gn~PA$ABq4MKG?vzKBLt>_|qi( z1v>JC8zM5!J#;;Mgj+P^7bVyW4E#R9K7iPK^s-bub!Nq=i-Ix*e~XrIC|lF z%CmhMqsm3IYSHl6L%bRXaRPg#BXyu?%Is4bAze@`8oz(ow|HJTp{WFk?0NMSz=Q?Cf8Oqa}z=Ll2d-d`DYBkA9U$b)@yC|NUbTKuXx;FaGke}sb$M5 zn6d}UcIgAE-?r&bk>0HZstGm7|9t{?+dB!mmR7L#h|E_Lz{}uGFSU-25wsPy=P}S; z&h{bw(2!wWZ6mZTXw^1EBeyEQYJU8;P$0yPouMfEO1>|}K$*sJa(GUZ`!D_K)$b)#fXBG$q9NsfRa6`Q* zht1m4#Lqpz?sb@5{1>{n7Uh?sw0mr`OB&kJC+7DOV%(ETt5@`8L*MDSwJKieOqQCA z%3?;c^m-~CUJU3nwLD)-vWg0LZhx+Xfl>7IfFHuI4We@d=)ZzUIL1SH4-~IK_v(wn zk?LM}UdiRv1KAljou&xYl^^S_hi1N;y>6Yf6(j zE(lUuUX!3ngcml!i9n4beG^>viDCrBdPfUIlS0fSqeb%;A~W@Ct|#35>*c6g$qJ+i z;&Ja07}Z0Zy-20xSY^L=(2GeyHzDQ`3o#}V@BGriJWy{!{*jvgg;#zeP3g4<0x7ETb{w0Thf#GNU+a1iI4;$$J9p^JY zH0PyL&UL^9HB8ESprftSPI{d!;e}CXCrT2KHB&8QR`YwtMj{PA>={8HmI^;bUQwD- zGyW?gT9~X&dS}!}k;*6!pv!)EzD}@$FGg9eGppAh54L;zmtS!w(3b{w6TrRT6Df4A z!v%5)oWFOmU`64p*K!*9?#mEfRh_VAwL50E9|+yWJkQEeG&)DHk^!v5;$0Gu{0D^V zXe(HU&D8=q!AgFcfn}FolsdKL2PMIWO4DOn6_G`N-1|6w&TP~VJnLo9Ol+b6rVw)b zpuj9ju}UGBxOsr|92Os@4dAS8mCu#U=m)3rmq7l|Cf|N=ub*Q7___x%g7;6ll#&*b zpR&@{gxco>86hiw$*c96kzxSF1rv!c>4UAnvnR+imN?!}Z^}01fo1TadRXBJzIKu7 zHRWIozxuq?S2|@CIP}}$YpGSQNyYdDwdf-8cQ^sm;IY3DmHk-btxNz{$SWV6qrV6C z1pMTAr~{~x+X+^ZkH-9RQ5mTuTdP~bCK}l4vVtr<7Q*+)SPK)JNLXudOJLducVhF+ zG@~|&Y{#rlL?&9{3QATXYia9gwX#a6WoS_X?FwccD6mXI^$%b@q$zf>5T$e=giJ}@ zc+EqzRu`}cN>a$@iqQ%olZ=YpIcb7&4bQ6AmE7{$FmbkQwFw0$m=Q3UAFu+dMJBP< z^B%%`C1cTmuZCfeQ`+!ArH0W_r>P61auPDI27v&)_ou0zvK>qu|aG7lQ`!xz6V$|7bB zC6?EP6FzPtMsf&MML#+K3T-@C6}EuZw?C6@a~FFvYK&)+D2l{?H?9eqU*C zDN0=!PBWEPf9yG$e$>X-lj3)N!HyuEer$BzaI5OIg!HeKdsKk%< z^v?Qc){hIQg>_q2unu2ctcKvh;{aFlH9IR$&&|~)!0IPig@XAc@aNFV{SM#mhg-}~ zuCSIuWa|^8YLQ}r~H^MJ{ zlWtK0mor?`sCrEp-Y@A@FXDTLdq%Y#8T5JWMFQ2lR$h(KYKyl)OQmT&y?%cC6a0qd zX1C{K_)wF>E8q-U*STCJPkZ;@)!v$VE+w=Xt5| z-pN=z&8NoDB>W5`Q7$ey`BHI3VMhobDiXtfR(o5WroJBnHzj?2Jc%>A}HsU`N1In zxstq0*XxdI1iq#LcmZz<-`uEakXnxTzQ~7Waik#Ybmv8qu|QVPG98U)r?`(J&xTT4 zapq9r^gdNvvQinp{^q8p5K~Yuc~q?rKkxi+c}Q={<^QKF{fX5ZbV?>t@E*Pp>d0YR>#|hbm31r`+PKtKM&&P_oETmV6MY$ z&zm~U!zBXXkYRWN{G-C3+3Sa_;jjSyTTJ_7+w`5u=<2e9XOHKVgE7H}%7t}RL>9q? z^icBW&078r-_@^ye$}*%XGDq+27r)lg3>(yo8IgI?IZuK5dQt++fUs^xq$5|+N?Xe zEF_2#eDEY9Uz6)8y_SwY9nxRK>P-2|S;M#KDrI5Z5{Bm8lkFW_n%zil$}sPNY4D*6 z`W{w=WEJXy$9efh<)vrRElS|B|4t)xFBGo_l)^8Gi0>Wl8O8NF39|^EEOYlH3w24a zop_CkdbL==OS4UJer9iMu($FJ3k08sYmXK59+yQYt>;_iCE;8=S8R++LV4z|a|d|v zsc_9gdlfWEOPlgs^$!o4fYQ`N6hUWeA<7YS9=k#jLS|}eAk-iXmfO^txez#UuJMB= zI2ToSAcRbU$jntvdT8~^G+uxafWg)Lg_cRPDf^tPb42=TQM0p9E5g2&+!F5EcG)CZfA7w0EjN`E>V}sKoDn2CLn^6@EbE!ZbbB$RdUDc03$fUpU zau=VfFk(nNyyLU$Z;~_8f4Zyk&JLIL-!*29SUgipA`GJ zbPas=%bzR6cL_%R(tEy?=SuiR@H02)x@oDFa;e!q#n0MxniMWNwayGq#@7Z^X2o*49J+=b=S z(v9U!8M+*p1|O=R?_pI)R-rDq$>G7H0rkrL%yl3At((*P6 zvj{{|vs;&Y0vW33=gy)W@&Vk>LX4pr0NW&}(Ua%4wZEWVl%S*e)G+|OEHoajWRxn9 zORjiiJ7GsdRD(xkc%E1F%np7DXEeotG+Il6=;1xXnQ|ujy`nNCt5C6Y$Dl;eXq1i} zUAPHZlt7!k)`4b?71=+CV+u_(MWPk>P%M&#Q|sDCOr@hocGgCc6dlr9VeBDtuVV74 zRy8|Gg|$*wu-FtvNeb65sM_;HaXBU>eF0-nU`q=FG>&?tj{^Sr)!GzYuho1^ zq*{S-cZ%gqs%fr&G6T{5_Oux|{H5pG!l2O&L4R#Tq*o&$=M7v~|WF=>nt&FHku;l3W)$B&Js?zTWT)M+AHZ#(%?sY~r8N zqgZ_Ge>v?^|5in7>?4s5@aDM6qRVhv>BKrKhJ^xmg4U>|2h(@3c$%?}LkR`Z%f7ja zQ6u0!c;&A9+TD>PYY=_1BQ#Gsf_B?skz#d9zgEVytN{!E`LXZg^PQ(PjlJ&cY`X^g z17Q=LD~7>s1BeB{>qeQi}C2SD|alTtMT^xoSt>|Iykk@e5dXWf<0n&Vi3RU z;JR{_F@KgVrJoUmsn!x~Og?-0rU*^{;o_*8IU#{5uK%KX2F1{Tm@Z zY%C;#&+cQB>nXjw&0le@FGO=DLGlnIPG*#9@R(R-GyB44%Li7^B?qR#hw5BzAXO1B z#8H19lwT~W?IV+vSd_q}{!F9FxhS4DW$k;|ltymiT}p2R(h&^H~JkW z4#Q>ucQ*5KtrjbI^!EGm(M!dJ^+oB}n+vzP4+*z2oazzTcM63MLxuIt2hcWd4GmK?KsA(=oKIm*>2zSqtM^)Rp5}p?#YMzn` z*DM_{o@r7_^OU6VJ>5BgI`MGkpMW>)wr65w*iZ^kCeOZ9J5-VbQEfXq0TWhmO)Hw$ ziT$#0{+*#Vm#jc%C9L4pSYaOl%S)13g@e~&o-#iApd_c(sK0-!Irrx`&wS2@_!$-@ zBSp@1!b*O?{7ZxEU~`JNIwVx_)b2Q<`hkE9BOeU#=z{UI@+Z%}cApW?#L6}LT3xU9 ze3S=Y7v{_Lv4eZ#-#8FYW>FwCR@;YqTH`b-QVX29P!s&{#RyaV;d;;4Gqham4Se>6 z%S+SNLsGHG72G-|Rpc$*IutDU7D&HxeF&ji%t5u#kkjck6FE58z+swW?O4!zunYu# z9G>QDwEy^&cU^Y=VlVO{>2hAJ9eg2d`_d|LQ^5Vv6jv3ydPCtr=~fbF{G4;W3Ngvj5Zl!_sr;kGK%k&O>mQ*`k0K`VMqU2a5hc0ME07)eCc%4E`8@+ z9@i^dDE+Z#x<84oZ7-@9rR-Z5QL|+Ir$=R}$3+y@z9gXd`={T3|DFFv2wnF3yKXoA z(I^b9Apk$JH?_%58TFP?iGUd;>5`GZW0Rb3g4d$ab^^znYf?|h=N5|+AP7&b0k3Sy zDg@lSu)RpW^r>{qDtH{=Ory%VD1J9(?OET6#~hEe!KNSt5lFRWDENZd%(F*EU*H}V z;##ar61=NmCknHR$sS` z6`-xOJXGL$7#*tCvZzLYv=v0c_s7=a@&jA@y?m;g8wXl5IuYDe z`(x5(AdoyUMG!YC6{(2%1_ktP= z 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 E3F*N$W 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ᗺ`v%&OWELjS{6v_xu{D5E&35nnT<*o{ z^z$*<*rbpLpR_}~m^P$|dTle1(qyqaZ?hu&kTkSQ zm{!l6ouCw(QHlba~X|CJ%)lm2K67Sh*RkiLbL$`E+sZWg*) zYmgl)lifQyv@PXmVn#s6ga)}X9v{)|StO1=KCqxHMh9kO8JB_6{5dsW-3T%gP5Olw zcuPeSr%>?1oi~NE8vm+oL8Z7I&x?+quGL_iw@F^Z&~Hvu!Z zq`BK-tBT0ZhTu^(@S41|?YFzOofm{C>iP5O`ob666ww*rS17n1B%-geT5l0ztY(-A zHpk5Z_}U&HJuY@dPv%MsVUAi-V8O_N(fTPN`=QG{BX zqn%TP(4c&Ew(!g;L8WJ(%1y%l;pX_V6SdB7k(k?D1Dj5<(>P9PUtJe?r7x%V&}E?r zq|b1;;;qY`RDowWWA1%j)L4>`36o+vWLcpEjc+ce$=9?ajQ77Tmq(q`PDz9xrvHn}&m=BuuW>ezj^kIl?GSwB@5_nC$ zLB4x#iI)flLqC<%b9$Jm?7dAXl55{dV^y3}@07`mEbk;X=Df9umWEDpGp@Oyij8?F zFwRblAc_G?C&zN`;=YDR+7DI4*b;0Dn18(ir7{9z2hMFpfT`srgDMu~eC_&-q<}Vv zPun-N@1wTwCGq$J?7G1cz0xfS9|}%<&Zi5c!&YJ+6xDn#+1*K1;C>ls*l_Vo7)%k* z>J_qP;AjxuFU%^lx3~1y7D7Y$LIaNH6=R`^-Utim^1`f#{=D8Q4TeF3mteF>;~JrG zsB2kDGk6v}$7^2f%xl=H10_%%?5ZI}A*5gr5IW+#bCXw?8vF)BsjEt#w>kCXBg^Gu zzl!}FDl^pFBJUt7Co{=%EuB=VP_3a{zH4=S`7*ypHi8`J(B&EL5-fh-r;8%#QHJd& zS!ooVj@Pij)PdBexA}-;SdRj-QJ(}%H21YJ#cusR?7IW?z(!Q{2r@(-45?T$Y^y%z zC&CR(w?UZ_m5cO=0@=g2^Z;ei%=2;$_Y8D6>MG!Rvg2RzGFVR1J8j?>t3mIdxNRdW$d}c_`s#$_$6h zXa9#;PF&c00hLES>}_5^LLes}r)co2Z#Xg4vC<-}3law#67Vp^H4|j;3ZQjmGMr$* zaAP5(P&}{bSb6G2S{)qKy0U=8lJ0=FaSJh8r&`g1mMkPsr9K~glH{3C~Jn-h!9WB2S)VBl!Z+tT* z6?UUiunYnS0AjK#Z(P6o_^CnERpNJo@n3dtdjWdhf+p@-cqPp4WiDmfIrZ+TTQtgd zJIE!KshY5HDd5;r z78YF;pyBr#T zpKy+u3xS6Bm18p5O!9nh1c=?nPG;S_Y5OC<+|ECT_@mh=68DphWv@g92Yc>pGFs$W^Na6E ztI;eK-z}HdktSwhvjlaQ3@-%-b`^@pGbF?%ZZ9|G$;uI6ZgTk~VA`0})S0DsXTGL# z{h?+ClXhVjjiKAt`X8fAx5-GWmMRc`yVX9X9h7^C)w=?=pbUjMuWo|UT1^~NoDX9i zx4%FxK*>%)HH#Al0CaAKpY>*!ve9B<>+>n*l-)MRM2Z12_kAOCq8rgvdoSF%COF6{ zh&;jdfBZD@o4~_2bFTNP_Y9NNQV_|s-!%dpS`d9_(K2Zj8p<2DG)ch0YU5}3K{U0F zL|nnXE-$C`#k&5(I1liEO)U>D4|iY+n(%ap|M+wvbQTYPiNjAaFc+z4R7%$G-87*8 zKQzWW$D5&*AiN9NvzCU}tSLZdT_z-ymO;Nb1AFMmn-(qPRV>_zSkiaG;My(0P0&Sj zN&R(+;@PxbIMHyIZmi+xa%gjq9k;7JF?Y<4y9zU|kEf~1&sPqi2NOmV_xzOfW?t`s zl){zg(UALQz8S8P7-0hI>_GuJ1M7?rCY~_JEpvl99kROtU?=S91+BNDHq<+AQ}z;C zgidkb|6!z%ES{mV8_*u}e+WDmrf{`y=60ylQP8NHN=;!BuQbg14I{PTe&MviXO4~K z=00CtnQbMs8OCNy?0HlT5B1$JBr}Stp@>n={@=S~< z6|OKUZCdc)8`kuerHA>5uPoj;w$KfGa3{5n%pqUi(uB+|(fZHVig%yev$MZ%o}Z6y zDm~2W9eYW?YETua_~av$IL{R`gXl=*kJQVCfIzE!JXX{(Bh;EH@jflix#-EKXwm*> zr6f*LQ$pAy&Nw1#_tu!7ByyI?{?U9$7Vvn{O>HFlXTst58n;r{Mk_&V@y;`9xAS$Y ze-UR{1LsN(WF%`kEZmqbV>Y|GmmlQH6+{rteD0C1*XT-sKQWt7QKN4x7S&V}4XIJ+WkeDU);Ra|0@swj z6Ak)f%tfn(OW0vnI8gEHBYZB1R=nS=*2qG<(@ZR4nsIhMM+^)=tS-0ybh-1>;)dTB_ni+3LQBTQWJq z*N9LtnXz;S8mLbg%47rMVxrX3O1r|CASy&Bk}#8cO7ciMHnXuqZ%ixK^AL^xC7g9= zqj#M9ux0mWi55<&_yXthP5Hc!%2pz^;OI2^54!dC+K}`1|KCV5(oAJGId}VYr_dA# zm;{M&b9+61uxI&*{unwX%SkTIKi#vjyc(`JMJ@PP`H{<@y{Is~3uB_FA-v$wnL0P> zSa7tDzR!BWHf>Djn z&ilG?h0y6lo{0Ma1n6u@j#NN}ev&3g@p zuvX^_Ui6GwkK{xF%Sl?Han_7VD|QUwR0XFpN#X9|f&7H!lQGrb!(w=eZmI0%l6~m& z<>(6$|CrODz=GR>SlsWL_Kqd@Ede{5L$c_yZF(S!22SoIbH5fGJz%-|D`RPkX0`J@ z1;VFUmvhP8cTz7<=F0zG5Eo2$ujNV>8Rg1SDv3a7X5hdO-pmRfVR6AYw^vHoLG~<{ zvH46WY48yKmU-BzPQ~^Nltc7Udo<#|m0K_s)xBG0b-~9Q2Qq^Dju+F4n2F5 z(ol8k?VFKI$7zbge-hAQ@YO+)uz$E#c}*Mwj3_oH;h77sVmM4w?6EC7ZiDtH9n zv9dQxeccOkQ}lhqFb1S(zuaH}Zw)U_b3Z-Bo|GaU_W=Fc>l`Xv)&@BlL7+9TKV*Z8 zDJZ?x>EItxvA^(c^az!=RfMtR5u#9mfo74i%nG`L2ABYR2?YtyH-1ddO44_0Ob4-0 zoC~Ii7CBC@uRGB!LLQ4#?l{t*j~17&mwtm;&*v=jksh?!fdPXsZZ2I{MnQ8dU=<93 z1UepC%nn&^f3jY@!y2})8@=qLmr;&vnyY^O5UOf?wiv$@bi_5hu$fbfqzvEIz7q3m z)+4P<1&H-o-VMEV*H+cs&W+3-Dn~Qa;UR(U|3u{jqIOM9i0R%1I7AD=YVs@OZpP#< zoK#F9B~xLbvc)WibAR&y;+^ro&cxk|ofc^-*J$%{XBZ}Y)bEAL2v3yFb5SD%G5PV# zGdL+YDoo+FsGqVww1Tbwbr;FWo`fv( z8f(pLD541QAPv~Wli)A@mHa%=g*8%odg5RR9CZmwhG*z>CzsUG_DpgK5_ZJ-)C z*+Z3C+m$vVLXve%~r8~xzwK6^<{v{V6V zs+`JIXPbzi4hdN9c{c=An5~n(;dP<}$$gR$uK=&%hr`&|2U434;DY|=0peHg%5pBc z5cl&KoC}$!$nJAE&nQf_kL3xMMZthW!T=SBBq$sZN@~DT@Z?c;#GyR0%anncq24Pq zV6W`9wo1RjHK?3tCp65=V*~hOsM`~-M;B@q_|N8~Cm%!?0I9cwB1Z6fOQ*xU zIHShh#u8||wU&?3NwYUDOvt}Yc8Y^Pn~qC8jIx_|!2|j9Ngr<(WnI6A=Oe>3moL(p zYWG0PbcB~@Z>?#wm3-;LMLtFBG^K+gH__~c@rrdYO%~r7d>iBq=!e3nF!c-+>EW+W zsol033+B*DG?T}>q?FgP7#)2(!LvND zhVwQf)SI#Po`l3C4AJdDYe!(f3j_!7jU6?shj!$Z!TmQJp zjfljkZ}OUf%3?bn>+qHpYKMl>8d(rqq~Tiko&p?)WN|Ofk)&v}vs# zuF!|i>%scJfDH)&3u^77SjqV?DU6=G5h1($c(7a0nfBPyLI& zExo>wP^sdQPy-m%qc~0F7N7Uw86`b-=ofUio0FuT2`~e-A=G^$OVRp{f(@8q&7o{t zirldO0XM2oJkTQ`Hu+b-OUghX-*W@o|6u^<=jChPS^GP#x!kfVM~Wq2Y_341n?JI3 zQ>)`}7ls9yL5oW9=m~pi)!xm{cuEFBxZ*ENy`~0oEoOymfcA!Anv>e2i2ju!r|~$G z=dy(vsSTGQN2slS=qW%@6FJ%ntqB-8kfFkanIV&Z4g{84S$J>r=7m_ae>&WSu!IqAaN05hAvtDG?^MK!#2{(;K*oCrf z4$uepsY9Sfads}xRj^s|u}??ub^x!%Za03M@6JB1;F|tXtDyrKXeYzedt!Q6%Y*0% zft3HS-hU(TN5!h~o2k29cv|p$JeUhB7)B_8V17fYIt|qlmG-CkKtjrs8QNw8{Puy+ zkGL==*tn6))l8dL7Lfop`Ry#*F9OCu?~H1J6vTud$`=}iHP5;9B$7(5M}DJhS%)$c z(=6T@tikJ!t6~ofT8P#cp08CX*to=qkn1;3=-w4`i%2X$$xJYrhBCmCyIB9LylUIW ziDSm$zQLG1^;7z0tisSC0g<8Hgi}76!5QrVM24A8z!sZ^RV7fM;-rr>iiIAs@7W&p^-g_`4r{f?z3!OTEU0ArWbvzf20@*h7)c;A&h8ziG+&oi>WoX8V6XR z4wgQtA091dV2*Qwkgnnq+|k%1cj-3|V-qB)L;Oo)6lTpJ0ai(|NNrE~PriUDEI@QZ zk8}uY!z>5tvhYtB(ol#u&s1e(n$r2oJ#pkT5X*L76Ygs z!sbEIJ7;Oo{Mcq^bcUV7mNY?$|% zzLfkbjB)Z5U@R%hicu{Lfudo}!(TGhDB9vx#64Ruh()P}RqBI63IW z{c4pyc!3^)Sy#=Tvu6(k+b=@RNup=6o9f;ZhHYMu=^;Z5K#>rUbfE`1GZ)^1dAx^>`<+S^ouj~>sZ}! zRP3>+ToY@B#3oS-5O=K+j3htG{_f1}5yi*0Z0hc043$UhTZY18XbqjI(Xd@6v&3Ef z(qyYBI#3Yfct^*C*vfx^Bv4q+MWT-f2P7D%nxt$r!-E_XNH96)VfAHe+_|pm@F#*+ z_d-3s^9iqgtjPsZRMRX@GpqbF{m>dfvzAF!D0^OD*}Hz8s?N$wZc6f(y7@rqUlQnW zmx<n?mn(to4;(?d{=sjB-(GW0T;>RSw*FK7T*Eg>&f6Fo7tna>Kiol1KJ8s1mvh zvH^bJJDj08EnQJ183N3Q2e^P)i9C{7xWIyTVS0? z^KX;qZ3*)`Pd;2~3MbD{ncAGRM6bq%`13x&Fe6FeTC@}Fev@9Sa81ghouDQjjwL1{ z4Gjef*k75_LGXboIEAX^dyr(%vWSDgyUx*jZ6z-V);){IQ(stnr%xSy+5j2T-paV= zZ#D}j&yfa{>3DBe?c=&MFVa7RiPoPal!;&}ai=!Stp!g6!)4J+pFk|z*r}spTsL^z2YNp;G(#7iY?RM1|_38I*OO6 zYt?KuMiDTkf+0gLpiOqFPmt^c2vRE0wJ7d2F_wezL>&ab3O%bOM)7o~jp=f`A)Kkh zl*=#eFK&1IU7HoI4$>Di>YYzihUm(XJ~aq8r`3T(9%|Ye9%5yMp44CAL`%tWTnb1> zQ)(SdY*V&@dgwKyN>zU8a6rDY^f}sTChwT492KS1;X$S`eh<)3nGrtO4+tae3cfaV zEj7B35+_1zoDL+Qhbt+mPj80?sx&)BY6&tNlYQV(i|wjm$PRgsZ$WCB(uunzuL zu&Z5}Qy^d+H)gFyzd!M14hMgW9GCHj`(og4!@58}F>?F6d$F*_(l9n^<0XD*vZiI7 z({fkP`WuWw)PG zUCR8FESV=1-o3ov=G2DLkGx!Bvjr0hdu7P(NjXmabqnYXx`4q$t3{7TCynR6-fN5G z)0+Ff$y?It2CWE2)aNttTQPmO4#$NQAAttqV)}EF=Lo4t92)Dz$uKD@6tcdAg6HtC zec9C<+?!Y(IMeY;02Vmy;U?m(Fu>x~*y?TICq8eo`Wety^ER=Fdxw!n0bWj&+ubw% z=3AK+e8SIlpMuskdG2dwSv14Th)kD%%YFin?Lnhhu9^3%E5fjAuZfH9wP+=Ehgn z$1BRnusyC1ipe{NQFU&%LNKYs+aB8UX|W1rV9wjs0=lJfF+S}7LF5yFh`VzbiD_gi z-#L61cM;=B1kPLPsJ&xP5|&9Piv7RP2s>Q=2B0Zz+|Bl+W*&oKz)9CYmg&)p)$W7B z4eBgI!j3NkJY9jqNNLUm>NgnRwciK=l?sY3N9{}j_Yv8zYJgl9cg^EP-@RCl^Oox3 zHbavJXsI!Ua0b1Lf<;D%N>`CT#B=kTrH@bDQFrEMSU;^WU3IPV=#c}>OINf)Gk7K7 z>hrEre;EaQtu2d+B%>n|{3ojAW4pgUY6bo!Bc2-OeGK^OA3ld;!C&QkP0xjdwTcQ}pBfwY{ z@IH5=L`n1OJ5==ZWyi`e^{HOpWt^o=%XyykYLm$~dqJf2?dwr+q}4f|6s>ZpM8s2Y zc&p%n0Xjw?W%fC*%W_<=RWPE+Ny6lAWcXwOPF<2mv+8opc#|9)4dxv-Ex^;YthnEx z0Lu-+?>0Y@?uThWdir+rg?h{V$V|^_o0JwUjqOcdM0?+_aY%NA3nk=uLd3e}9SsRj zK7iVbOWIgvOHrthH3CQH#iCiRgA#9JuFnvyvKLz_-|DyVLC7IVOtGG`6FI$J-xTkP zuF$PDF?leCPb~a2K0df+W4OF!6VC(>0)o*+L0UqK=0-|ms7#!4q1(l^(~#FNN?A`7 zxO0?|`Q@L1NizP)x>UTXh6Vcck7ZlI^movPn6vgYBF}z`wAs1wAc|_%(CF`34QMV< z9DGu{nvH9~cn06~X`GiEfuV`2bPgeAS^gWCn8QJjBvId89hTE9JtOo8RAPt(nH%HE zjFFk~vymj6Bzjj}xkMlea4**Wdo#8xH$}gTbK~^a{y~>vb`DC5ZVY2qfbAOVsI9 z)If=ig~W#+MT2S~V=Pl`OX=f=2AS?-!EOKfBTBPa!kXbX_{9i~J)y#P7x)DvmE;nS zI1DiM$#7oVgR6i70ck}PNlY=Mk^;FC!np&C`w}=_36ss=l*m_VTz&S5DH3qRcEo<1 zVhuOGS$JMEKDS^Z$@tuCklOz#>nph8>VhEg!QEW~F@n3hdkF3j+?@b3KuFNw?gUM6 z8{9R)B{&QO2=1=4k8kDdo}FJXuTS6V>gu|E8Ht0HWzIc5S7cJrHv*QQpHGu|n;+G4 z$fsnc(25408-y>fDhK*l-T=8Y^eK>$h3bI~-___;res&8oj?VJF*r2Mid-cX=Y3F` z?kO_n50YcDJ(~%G;r=>%&{{?rBrDn+$6vsNL3<+xwUW@5YYS7Y$LUDzd>&Y{GF%p< z=+t3V?7ddMk#1DziNmFRO67#1tX0Ixm9U;8xrX#2Nm=qRLKOxS*S7H5X0)RdcFJ5 zEWG#nU7G_cfJz+%v;*%9%?!5HX@kajhn+bp|ep69CDx$GJ<- z3-YS2$fT~h@;3Sj{Q|3oTraQp53f)e@+H=?{@9EcsxpbhAE&8MBwY-i^eMu(R%q#g zP4B0A-`QSt^VgVvj#;mkj+f!V&^+8gOp2%A6Q?4dY*bA%;AG8dz&QROQ6y6$llTsl zBTJO4m@Xz<-KMxmpKV@63&Ld@*rFBvWFWlahvZMpL-W>$6LA!kSyG=Is%6e*8vbM|qoZ6d<}~ZhD-rB4&Ut8sGY5)DO-YGJVi2!2;J#&~pt+;CsK^ zlSH4fUn@KFtMAQdqVmkv`;-r)3cyXuA)&6;fL{W05%kIR|B3 z6(cz8XHn79v+xokL}A*8zVVA^A`qw_WDf{@w1p9z-QB%@iotJc%TE>XWArQ*l6%y|EIh84 z=*u9L_aI?`y2P=)jV%41jGtIbtp$wm6f+r}&6V&+y%revtSplM{i3+UupoNzm+wq{ z#%+byVXuqp@Q8!)6^TUV;)2c#*JO!AyUd5^pA>S!;~)OG-<&nA8|oKi{o&?UmLESw zDW({7ZHoSBLV{nrP}9a(@Ct4I!O=`JCe_ORnaU9T%nQgwep{I0B{`yaxmsJJ|Ay}4RHQUjv12Qn=CA50XYB68KW+vYpd){!o_xpjq(Xf z=lWuViTJP3vXDVR!%0kQL}Lw1;<{oKFaRuPhgQRD?z3*_wEdxgbE>}eFKIE#q*3a- z?`kYMb~#y^$7E$(+K7romAij}D{(!I)=`U)Z0GE)`u{sTzpcXZ`E?80j7y5mg^w7W z_kzaaD|@Hv5e7_osE;Yw_(!d`ihGI~=U&`t#slAfhP6G{DloYEZL+l8lBL&M59B7l zo5u1-U$+{2d|z|?@DqrwZ}#zwR&N~*{YJ)EO(;m?aDo8WjLz)gjOG{n2`$hxAO3Pd z6bewG@$xEPvMZ!jwVweRLX~&y?#xIfq#ygQglv^Zgr$a}EI1`u-rKBrXb}FW!PKLN z`oxPzesKIzZZM|B67HMWxX+@nsQM?6(O;)fN5;b#M!gH}2S!L;r+((N709*9cEfwZ z#3;b$4l`0Zpj4tv08STe@9gp;Tbq@JKfCI!Kg~$uE)7lMHHncO!F(Jk4y;j%;R^~! z3WT9az(9&IiX)hN-#E)Sm0OYOJGcJ*R&f5NyKn``btiT=Cd$Mt*u<*{w~5>KMqxv@ z1u?-`YMIdX-acJri3>DoR>~4dcqoPQ35Pihc|ceaVY$ey)QL&`ABc~3eCqqyt(utj zXFJX_d{ujt|LgO&`cGowoVbA#@uv#>(fO`92%}dPtKBs|B3lKH*r1tU4&Wv;32?z6 zd`QdqgUq(BGe_!HgMOU}T+cV6v%%qW120)Orpm>liTF z*d9Yf+B-U&%Uw@ju;*DZdKGf98*~#tqbtz<%%t_nXlShNg}VnPkt8}DBN3#^eow}T z(?!|g$^KD#HXV*a`1kyyf-y8&4BeMm96dq$>!Z{owlt}CJeM$iXCY?#uP{e_mV#NTM(BLN;$ zu#~r_o||N(_TYSGv8f~?T_%o0C z$w9-CkSH08-cYU(r16oBMHpUCZ6;gqB=mk4Mf{rrJ%gC(*O`yQJD-De&py)&TFtZf zS(pWXWp(wN+DLGZPT?QrY}#}eQt?W1U*rWb>B#^}f`?7g zV@mR#>hT^uiH;$mQqoCAZziir`(`4|Pos;bk&q~uU(5bvcFx=b{U;5-Cpk1CKt9P&?QY!HiH>Rk+BMEa=NJlx8dFYj&i{!{=!KB zzU*vfzjP%9ocmUtHOmEAYJWe7J{>H&h~D3n+vFNG>S?Ncear`1z;YJ~myywjksfl= zaRZ||2jZe2rFb@jo|UYZDI7|fdEA<B#-ORlad4Gp~49Jf|&+;R4$*>OLxe{1I%3<^Fiyk&>+`uQcm@?!9+%DAi(>3BG%sj$# zOugVbKw={iDVGkW#juS#ZZ>d+mywIn(U)a0WTR7)ikk^i=AwAe~PtiHb>B>ZCS!UDV3>D|sE~R*(^Js)M zzmxC&Vy0?{X zLHf!qyo;IfPu5T}de-8>8>rREzgRl>Dc#D`ZRbs=b8g_(`qf30IQ$k&gkPz3!tFv- zR)cvln?LY^l9L0YFdWzp^`jW|*HXj~J%eCX)x^j1`k}73w}?nuII4shFA5ej zq7G(oFHG&3uY_lS3@PnvL(*9TT-DO3c!ZH_p@i7X*dhlS?m4KuGfWk-6|YghC*FSu za3@Lp!h2b~+0q|r!F8d8{M`I+u1%zKL4^tBj31!bzI-_k(IX=H!PBZ! zXgk;~39lu2)$L{8rvye|?*|EEnaB3ZC|Vr%;3#bFpe!RW0E@5JDp)ZV%-()RrWaKq zx65lKKf@ByedeyX+Pos^bb3^$(fcYfnMH|0!=7~#1_WcWYt^nhde2DfeY;UBZpQsKLF7pXSwJExq*C2e)wTd%ULGU(*dx^}ge2aXPBk9M- z-~=p!CYizvqGrP2_nF511PkZq(}%j9H|I;+xsb=Jm1FSs)!DE(++$N%>N{ma#Lf3X zHZ$Zh2TcHGPceN<_qgza(p-Ch7fX!$$p9^T@H>ed5T8B#sgG|dPM=R$Rbts<1=6hk z@Yd!Ig3-~8k1>;-d?VT=QMkujt-RZuT9-D45+k z5Nyrd?FWDUC5yx6=v0$UzG$~&Agx$BM5j*l*HBBk^76%*^Miy_{%@H~$r%<(?G*K$ z-w6t&7|clruXBF`nj92NGSq~jwH1f-AoSS9)yMtX4mCI4CKyxFR7fj|h1%q~C8T0{ z=6JI$k?*a8TYv?gR76#1zM`oO9 z+Wwpe0!C^|sQtbW-+SOTwkAi?!vx(s?hLrzFJ8)tV2bfFAeFH7Cg^{=LHYk#gRRX0 z<>j-1+;oVDBjz@)?9b8nCrb++l#=$}7|2CNjr7~Hj0*x&qXcR6HuMAzBSQEVT2GOQ zp=Pe!DvK;c8!1xCj1(_sHlo#S;sRW)e$+T_09dp;6~3Gi@m!$8ZS3M#15(s>1g4@{ z7$Pa7uwF6#Y5HGCr1tCJ;dblxicB1yPSeRf9-Nx{cu~^m^KY0dcVx3+U71@mnI4@? zaB|=|m(J`}EEr7?@je(^W8){90D(b{?_kJpn){y4W~jIbgaxA_d7sRZvs%Kyp~bfn z*A@`_@4kVmsEGwQ>smuEOuAc_B`H=P);+Z>qa@?Oy(mm0eHo-G0dNT+dI|yfY(^xD?K>E&1c< z#}Cev3q&F~rJ-Nj485F8P(Z`DqpjOgaiphr%KSvxBI#I_RufOPx=&~KeT z3rW+&Ys?nsOyD`nij>8ppHuje;uD^&$7}JdyM|*46t0l}$L3}~WF_3*4xgVN+Q1KE z52++pMManRgVQpZ)EmVg#^Jw~(fzKvR)cRAhurdI;2(fK%E^t zF(#|K7pJRFxr%7CRY)dlE$5nGp)0i<96$cjMy@j7sJqw_d1dNEo^w=(p3P(LKjuUI z=XGqP&NK6|2W1VX2^R6-qi?kbINm^)^14XOdkFxfEj%mWkKsoZb%(`QLxksY<#%CJ}yAoMPal4jz!rj z(PvOUn!;LpfJL(Om*hl)_p_pj3MMK*50_*MeH32VHCg=}*3r89j}vX(^*?i>*$lRv z*8QNKh7Z9OYD*|`s1t&#?Mj(-`#ft@zDH&kF%q|vYt4a8K9_%|tJ54!Z|N7krQ3cB zHyGC&FPC$=(9^PCuULlZx@zoT z+j{&_ynibqlK-3e1iQF&agkoEbC&R*ya4Zk&Ye0+-1`JeZF>)2ll8sign>0WZ{oy$FX-#}3XS7w? zY_&FmowFPXp(coR_!)Utg?wMlYFTO|nvRYQdOt+3M}3{37_Z`#u!_?NOJFnn6%KR? zga&W<6&(|zb#&Qs+(C<#QXj|ehrPWefdZ!HTrjfGG%e9Zjm?`8Je4nr(-5_BfxEok zkt~X}B*Z$WQE4_^6TBrCZU;pI_W5{vH0EnW$ZBY_$+RDCjPV*jPKuA7gGvi;JZ?=_okK}2%;vJ&JngexWG>tv`c=Q5!& zQO%p;q%_ib>}|(WZl|CK4QJ&r(RuM23GY-bJ~At!>xYG|n-b~hy8pyF?I7LbHXMFs z0bC{*r!!!&N%;4U5ptDC2kW?P7(Yl2llha3Vbmr=#^D_t-msJKPTy*Q>neP_Wr! zA9uVgfo}?KT4eoq#nD<|s`P#|>N#4Ym4B01Ga)wzy@yyHu^Z#y2PTj4joqtVqO7?Q z2kvu%$$!Jsqnj&o3G6T7GmO99V&&;?yW+wB8J{Ps-u~N8lEhz9p$=3aCjpw z?N6)nI3jj%kGy&FthVFEY?$|3?@)3HDYtL7#lr&$Id6V275Db+jiFBMbLWF*}j zqN)oRyQ1ad;=VPfcQ(iIcse7^7BsPZY$@eGDK|M!r&-A2&~L{oyQiv)^3z8QPF9dWR z%4eO2m))HOsf4dJ@_nr%?lq5CT5R{-<@l()dAdLKNsS&bnmfKy@BS2w=?iXyz}K&` z**pC1Z`r|K_ZzzBpi|Swy{prW!DxJ>zgWPo9#pAEGf7@33vek2?@L7`p#!7IHn4iH zrC4@dyXX~zbf~h*mn=3z$)V!%MQueIPEsr^?fr?7hZTyPg{;=85asK(_Jw>FR+66a z6&q{N$UMlT7tb^08*m_L+U;0ltH0WfwuBDhvIcT#am^`A>p@op%tXX3pM2$xs_F=T zUjbNEDX||6uRruej(iI#Y%VAuYR$E!_#%`+cl3|kZV3ztn(^Ct<%XD2H4Y8ql@^>QPm3gh7JNpGkt?Z zmrmmB>J&tGHNb{nL%Ow)Ae$ok8ruJUJ6`~eAbOtrKRWp`VwJ$W@HQ)s%}~{?(qI%v zeEe(_>6CYni3Eo|V4VUss3AFz&LE=QGw|CpO$L8ogD3@aJ$O)4db8P5O2IQJ7^5w= zAtN8B)quCY-QB&N`)ihBvy@sGR5=0-0FLNz)ciV6WUL~F`SG`chyhITza{M~SP?@cVdbd9$!I2Bc zuYTZ=P{R)TXr8HZ7YGbsdZw%!)`)51OVN#XO2JB-DkuYO-b7}Fd}zlgw+FppOD+(& zaAW7KE@c18!8E{JoYdicfQ`wQuGnV5T2cAc74T_PCkMa6YP#@C$nMoW{0Wx+@}PCe z;jFAmfq|>LC81iPP$ome(t+_W$*IQf@3eE4vwfEEL$*m_#*t-)*D9&+ zKB{kJGCiVtqb3 zdw!uUU+eY`q+sHX2|heO(<~wyBlxKJA}VyEfd}n==Xy^rVW8HjXvBgg8YZf~1A_A9 zobJvK8Lv$U$~ng9@aov8r z((!P!^_AZgWYIz0*YO%<^~4b1YE5G)KIki-%!dAh@YH6pp`Ga6=dq1d8l|JYMP z2*oBy;Q#^MaS%DpCth(0^WWWI&S$-;t=_rCp9hER$9X_**%)lyBhdRN#&1*&a9u{+ zFU}_QL|Y>6lA-+@#FC#elDiXT*~+1|6Sl7i^jo@OClLQnHXcbZQzo?v5Al%o{?%^` zi${sn3C4tKZcDCtQR>*vR!xVR2bV-`y;4nQ$ zm$Vg#t_Qp)VEcCXN-Yld*nRB+x%YY6J6wX@%^seQi+8p|z%KTyD<&KZ(=g=baVhs$-_`7> z3iXv0A$DjMP++YdXq7PItoIW7%%9KstX;{u>lTmiq}9k*{R5FJ3D2?*0Phgf%~Y>xAPu({(&uE~T1+ z{qFZRfYmMMMSI>$bWn`TOw{j@bxm(F)1png` zGgvVj=Zi(Ox5O60JqOw_rtG+n>t0?UM@889>HAobkyj>Cl3+x3{g|3)9!f`#UN~Y2 zS6CmOSZ5gw!#j2{4R~|_!Zb$K2)i9UhIQPp9ItlRbr7L+98){Tb-gqY{OHR+v@aIs zPxDU>y>NtXa6QAU(dh|SOnTq?D2Y5o*JE%q3~~ax+GdBZfg0%^u3TW?jjG4v#<|$s zk(H#_tFP#0LF9dFhVw8A_nWkeZ*z&ql!18mj;b<%-0`yjPZbGhGhl9o5QU-MCA7O} zW;y^}m24}rV$yJy!1u!y_bzNFN-o{Hsx}%m1rnbx2QT|s-bOt zy}F|sX9@E0Imu&`b4T?<}RLt~)QkEmc9b4I9}zZbz3IN94@|uNG{so=^iofO)I=^b|As-|*yUXRvsNyfF9m zYcpArUZtMP~g4v1`=c_5)B)x^Vp!=#Ohsi5;Kh=$!>FcKff zetYX$N{@T|6Lccmaym>{ItYW`&=Qp&|6vlKRUVnBO6(lBQ2+E;{PPy@kj5Vqqt%WbNdcD zzeyeSqsHOuqZN-Cq+5O7W`A&15<oPpCY#RlMh-ugyg3*beM;9X1$U3~b>JWPEIL(g|x9Y&(CHC^N zG2`3t!>W}?*fc*PR?VID=6&Xlp`I#T+Lh3_K_#LItt`Oaze3PgC1 z1|Df0UQJI-hF!IU4|cD+nf4C&Q!}N3oBmnCyG|R9r-B?(=ZPfg0xe*uk~r<)I`ngh z0jzxKVZ74(xG`06?wBagB}*DF-)7BHT+ZpbT<>71KR>ln+#61rpaI*CCLQYi7rWtv zS!>PRISQwt+qqwqfUiIUay@*N3ChPS87{05m)L#gXs!JtCRdGaEP1A2d*wRSVup0~ z0$e-7Ydy(kjI}I$HV4ORo*qFHyBNq*vu5Ph|DgH;i3(1@cyyLx#JJ+YW zqj&vs$PP=;cb3*-__Hl}$V|8WP* z91A-u7>rUkKL7q^zEPXeDz;|sJl8-rzisON80>7AB`7Ag@SO&G%X{Y{_^kl^NFTDw zk#pFwz?I2BqM%RbM6-^P0%VR-e5vL!kJU8@lPqgZP)kQ)A3e3GSyohmzr+!IhY9YEO*j{Pn>wz0sIN$r)3~4xt)JjnAPnZ-P6zq2`$lM{4L}G zNUs4$=)lOKLutsNF0;8DZ!@y;alUVJG1vJ>kb zNzJUoVKGPZ?!J=-Du(Li-#!g%L%9=vgu48l6F2WI=h0$lVEUh<7Bxt3Cel2a*)UoVlNh|IDY5zzcc1n; zaIMMdIP10UPGIJDlw;ci%N_2A>s;huaenVu3|MjJ%ib&ur+16w(7?931r&sluBS(g zWAT|TO+*%>h-`$>Bbxi`*!?9P%PtK(hlmG#!bGO4{A7VySh0)P-s+t?Px*DAYT6Uq zzfV~2mFvX6=G0tLeCt`8+gaeKRabddNmgw+zTCesCDzL zPnu+(K*YHx?|4Ex61Bl{gd?CjghDLSL>>#7aWp=!e%yzl zJK~%O2d>qA%I`QQHG;>ub0c*E`DfiC+bE`ogZ^T^ec;~H#HgXjr+r}z9y2#3tV&gH zMzyL9_47VJu&7#czoOW&oQ?Vz;WpR_F?JsZy>IOZjHvmFb+o1c8UNBgZ+`X3;gkDK zv`%L~O;LXE|`L^=?r&G0ZSAm{Pi6IY}!1{WaRXe);XY!9|kvH z-8xMj8I5BXgD(PPRBX9}?Pc8AQbl#}XE*p{*eD#6tjnin3}ll~QIQ{{!5i1Zw~Q literal 0 HcmV?d00001 From 596cc3a645f86f3e38b40abdb3fb82cc0cae593e Mon Sep 17 00:00:00 2001 From: Dave Green <34277803+SoloByte@users.noreply.github.com> Date: Sun, 4 Aug 2024 21:59:48 +0200 Subject: [PATCH 05/47] [rcore][desktop_glfw] Set AUTO_ICONIFY flag to false per default (#4188) * GLFW AUTO_ICONIFY flag is now set to false per default. Previously AUTO_ICONIFY was only disabled if the user requested a Fullscreen window from the start. After that it was not possible to change this behavior on the user side anymore, even when changing to a Fullscreen window. The AUTO_ICONIFY causes problems on macOS. On macOS if the window is minimized because of AUTO_ICONIFY than the only way to restore it is to click on the icon in the dock. In other words when AUTO_ICONIFY is enabled alt/cmd-tabbing through windows does not work correctly. On windows it works even when AUTO_ICONIFY is enabled. Additionally if a raylib window is in Fullscreen mode on another monitor the AUTO_ICONIFY behavior is a problem because the user might want to window to stay on the monitor even if it loses focus. (problem on all OS's) AUTO_ICONIFY also restores the monitor hardware resolution if a fullscreen window loses focus. * Update rcore_desktop_glfw.c Extra space removed and comments updated with a space at the beginning --- src/platforms/rcore_desktop_glfw.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 8377b880ff4a..d64bf60db888 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1275,6 +1275,11 @@ int InitPlatform(void) //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers + // Disable GlFW auto iconify behaviour + // Auto Iconify automatically minimizes (iconifies) the window if the window loses focus + // additionally auto iconify restores the hardware resolution of the monitor if the window that loses focus is a fullscreen window + glfwWindowHint(GLFW_AUTO_ICONIFY, 0); + // Check window creation flags if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true; @@ -1454,11 +1459,6 @@ int InitPlatform(void) // No-fullscreen window creation bool requestWindowedFullscreen = (CORE.Window.screen.height == 0) && (CORE.Window.screen.width == 0); - // If we are windowed fullscreen, ensures that window does not minimize when focus is lost. - // This hinting code will not work if the user already specified the correct monitor dimensions; - // at this point we don't know the monitor's dimensions. (Though, how did the user then?) - if (requestWindowedFullscreen) glfwWindowHint(GLFW_AUTO_ICONIFY, 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; From b2d48ff17258bc22aa4453506e20fa7bde1bdd76 Mon Sep 17 00:00:00 2001 From: lnc3l0t Date: Sun, 4 Aug 2024 22:01:28 +0200 Subject: [PATCH 06/47] [build.zig] Override config.h definitions (#4193) * [build.zig] Overridable definitions from config.h The new Options field "config" holds a string the user can set in the format "-Dflag_a=1 -Dflag_b=0 ..." to override the values set in `config.h`. The file is parsed and the default values are appended to the compilation flags, if the user doesn't override them. The user string is appended to the compilation flags. The "-DEXTERNAL_CONFIG_FLAGS" is added to prevent "config.h" inclusion. Note: a certain format is assumed for the formatting of config.h Note: this commit references the closed issue #3516 * [build.zig] Only SUPPORT_* definitions are overridable Lines from `config.h` which contains "SUPPORT" are added to compilation after being parsed: - remove whitespace - format to preprocessor option https://gcc.gnu.org/onlinedocs/gcc/Preprocessor-Options.html The user supplied flags have priority over the ones read from the file. NOTE: extension to commit 4da7f82e6f912485351167da3bbc91807371fdee, the logic is simplified because the SUPPORT flags only have binary values, which makes them easier to parse. --- src/build.zig | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/build.zig b/src/build.zig index 2da5cbd04d37..2c9519a0e7d6 100644 --- a/src/build.zig +++ b/src/build.zig @@ -28,6 +28,7 @@ pub fn addRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. .shared = options.shared, .linux_display_backend = options.linux_display_backend, .opengl_version = options.opengl_version, + .config = options.config, }); const raylib = raylib_dep.artifact("raylib"); @@ -52,6 +53,36 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. "-DGL_SILENCE_DEPRECATION=199309L", "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/3674 }); + if (options.config) |config| { + const file = try std.fs.path.join(b.allocator, &.{ std.fs.path.dirname(@src().file) orelse ".", "config.h" }); + defer b.allocator.free(file); + const content = try std.fs.cwd().readFileAlloc(b.allocator, file, std.math.maxInt(usize)); + defer b.allocator.free(content); + + var lines = std.mem.split(u8, content, "\n"); + while (lines.next()) |line| { + if (!std.mem.containsAtLeast(u8, line, 1, "SUPPORT")) continue; + if (std.mem.startsWith(u8, line, "//")) continue; + + var flag = std.mem.trimLeft(u8, line, " \t"); // Trim whitespace + flag = flag["#define ".len - 1 ..]; // Remove #define + flag = std.mem.trimLeft(u8, flag, " \t"); // Trim whitespace + flag = flag[0..std.mem.indexOf(u8, flag, " ").?]; // Flag is only one word, so capture till space + flag = try std.fmt.allocPrint(b.allocator, "-D{s}", .{flag}); // Prepend with -D + + // If user specifies the flag skip it + if (std.mem.containsAtLeast(u8, config, 1, flag)) continue; + + // Append default value from config.h to compile flags + try raylib_flags_arr.append(b.allocator, flag); + } + + // Append config flags supplied by user to compile flags + try raylib_flags_arr.append(b.allocator, config); + + try raylib_flags_arr.append(b.allocator, "-DEXTERNAL_CONFIG_FLAGS"); + } + if (options.shared) { try raylib_flags_arr.appendSlice(b.allocator, shared_flags); } @@ -253,6 +284,7 @@ pub const Options = struct { shared: bool = false, linux_display_backend: LinuxDisplayBackend = .Both, opengl_version: OpenglVersion = .auto, + config: ?[]const u8 = null, raygui_dependency_name: []const u8 = "raygui", }; @@ -307,6 +339,7 @@ pub fn build(b: *std.Build) !void { .shared = b.option(bool, "shared", "Compile as shared library") orelse defaults.shared, .linux_display_backend = b.option(LinuxDisplayBackend, "linux_display_backend", "Linux display backend to use") orelse defaults.linux_display_backend, .opengl_version = b.option(OpenglVersion, "opengl_version", "OpenGL version to use") orelse defaults.opengl_version, + .config = b.option([]const u8, "config", "Compile with custom define macros overriding config.h") orelse null, }; const lib = try compileRaylib(b, target, optimize, options); From b657001e0d7119532d01de27ad69d962ad736119 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 4 Aug 2024 22:06:20 +0200 Subject: [PATCH 07/47] REVIEWED: shaders_vertex_displacement --- examples/Makefile | 2 +- examples/shaders/shaders_vertex_displacement.c | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index ee540606dab1..93b05a0c7caa 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -613,7 +613,7 @@ SHADERS = \ shaders/shaders_texture_tiling \ shaders/shaders_texture_waves \ shaders/shaders_write_depth \ - shaders/shaders_vertex_displacement + shaders/shaders_vertex_displacement AUDIO = \ audio/audio_mixed_processor \ diff --git a/examples/shaders/shaders_vertex_displacement.c b/examples/shaders/shaders_vertex_displacement.c index edce07ce7c0e..c211d349fa52 100644 --- a/examples/shaders/shaders_vertex_displacement.c +++ b/examples/shaders/shaders_vertex_displacement.c @@ -14,10 +14,8 @@ ********************************************************************************************/ #include "raylib.h" -#include "raymath.h" -#include "rlgl.h" -#include +#include "rlgl.h" #define RLIGHTS_IMPLEMENTATION #include "rlights.h" @@ -51,8 +49,7 @@ int main(void) // Load vertex and fragment shaders Shader shader = LoadShader( TextFormat("resources/shaders/glsl%i/vertex_displacement.vs", GLSL_VERSION), - TextFormat("resources/shaders/glsl%i/vertex_displacement.fs", GLSL_VERSION) - ); + TextFormat("resources/shaders/glsl%i/vertex_displacement.fs", GLSL_VERSION)); // Load perlin noise texture Image perlinNoiseImage = GenImagePerlinNoise(512, 512, 0, 0, 1.0f); @@ -111,7 +108,6 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - UnloadShader(shader); UnloadModel(planeModel); From 923f983719a0c06be9d64efb597034aca509bb4e Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 4 Aug 2024 22:08:38 +0200 Subject: [PATCH 08/47] REVIEWED: Possible overflow #4206 --- src/rlgl.h | 3 ++- src/rtextures.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 64c7a1ab6394..7eb1bb1d3622 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -4993,7 +4993,8 @@ static int rlGetPixelDataSize(int width, int height, int format) default: break; } - dataSize = width*height*bpp/8; // Total data size in bytes + char bytesPerPixel = bpp/8; + dataSize = width*height*bytesPerPixel; // Total data size in bytes // Most compressed formats works on 4x4 blocks, // if texture is smaller, minimum dataSize is 8 or 16 diff --git a/src/rtextures.c b/src/rtextures.c index 60ce58f4b09f..94ad81e2035f 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -5403,7 +5403,8 @@ int GetPixelDataSize(int width, int height, int format) default: break; } - dataSize = width*height*bpp/8; // Total data size in bytes + char bytesPerPixel = bpp/8; + dataSize = width*height*bytesPerPixel; // Total data size in bytes // Most compressed formats works on 4x4 blocks, // if texture is smaller, minimum dataSize is 8 or 16 From 9c2ba3bfb7166b71a852d4a4912363d0977c8676 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 4 Aug 2024 23:22:27 +0200 Subject: [PATCH 09/47] REVIEWED: possible overflow... again #4206 --- src/rlgl.h | 4 ++-- src/rtextures.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 7eb1bb1d3622..3ff6d7e2a750 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -4993,8 +4993,8 @@ static int rlGetPixelDataSize(int width, int height, int format) default: break; } - char bytesPerPixel = bpp/8; - dataSize = width*height*bytesPerPixel; // Total data size in bytes + float bytesPerPixel = (float)bpp/8.0f; + 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 diff --git a/src/rtextures.c b/src/rtextures.c index 94ad81e2035f..9359f9219842 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -5403,8 +5403,8 @@ int GetPixelDataSize(int width, int height, int format) default: break; } - char bytesPerPixel = bpp/8; - dataSize = width*height*bytesPerPixel; // Total data size in bytes + float bytesPerPixel = (float)bpp/8.0f; + 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 From 8b714e9dd9fb7ddeb13f21a13d61bd77eed2e2a9 Mon Sep 17 00:00:00 2001 From: lnc3l0t Date: Tue, 6 Aug 2024 18:28:33 +0200 Subject: [PATCH 10/47] [build.zig] check if wayland-scanner is installed (#4217) #4150 introduced a default value for linux_display_backend, which makes X11-only systems fail to build. --- src/build.zig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/build.zig b/src/build.zig index 2c9519a0e7d6..65379c03b4ab 100644 --- a/src/build.zig +++ b/src/build.zig @@ -155,6 +155,13 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. } if (options.linux_display_backend == .Wayland or options.linux_display_backend == .Both) { + _ = b.findProgram(&.{"wayland-scanner"}, &.{}) catch { + std.log.err( + \\ Wayland may not be installed on the system. + \\ You can switch to X11 in your `build.zig` by changing `Options.linux_display_backend` + , .{}); + @panic("No Wayland"); + }; raylib.defineCMacro("_GLFW_WAYLAND", null); raylib.linkSystemLibrary("wayland-client"); raylib.linkSystemLibrary("wayland-cursor"); From 4b84b5563e53d6ef2c37795bc91f7088313c8d9e Mon Sep 17 00:00:00 2001 From: Anthony Carbajal <5776225+CrackedPixel@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:29:10 -0500 Subject: [PATCH 11/47] Update audio mixed processor (#4214) * updated audio mixed processor * remove float cast, better parenthesis --- examples/audio/audio_mixed_processor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/audio/audio_mixed_processor.c b/examples/audio/audio_mixed_processor.c index fd970dd19b0a..2df381b26552 100644 --- a/examples/audio/audio_mixed_processor.c +++ b/examples/audio/audio_mixed_processor.c @@ -97,7 +97,7 @@ int main(void) DrawRectangle(199, 199, 402, 34, LIGHTGRAY); for (int i = 0; i < 400; i++) { - DrawLine(201 + i, 232 - (int)averageVolume[i] * 32, 201 + i, 232, MAROON); + DrawLine(201 + i, 232 - (averageVolume[i] * 32), 201 + i, 232, MAROON); } DrawRectangleLines(199, 199, 402, 34, GRAY); From b44b759b8f646dfd7823978b59d2cf0f700e14a7 Mon Sep 17 00:00:00 2001 From: CDM15y Date: Tue, 6 Aug 2024 17:30:15 +0100 Subject: [PATCH 12/47] [examples][shaders_raymarching] Add `raymarching.fs` for GLSL120 (#4183) * Create raymarching.fs * Update raymarching.fs * Update raymarching.fs * Update raymarching.fs Remove `fragColor` as it is unused Move the license to the top of the code to improve readability. --- .../resources/shaders/glsl120/raymarching.fs | 451 ++++++++++++++++++ 1 file changed, 451 insertions(+) create mode 100644 examples/shaders/resources/shaders/glsl120/raymarching.fs diff --git a/examples/shaders/resources/shaders/glsl120/raymarching.fs b/examples/shaders/resources/shaders/glsl120/raymarching.fs new file mode 100644 index 000000000000..d3180ac12a0d --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/raymarching.fs @@ -0,0 +1,451 @@ +// The MIT License +// Copyright © 2013 Inigo Quilez +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// 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 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// A list of useful distance function to simple primitives, and an example on how to +// do some interesting boolean operations, repetition and displacement. +// +// More info here: http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm + +#version 120 + +// Input vertex attributes (from vertex shader) +varying vec2 fragTexCoord; + +uniform vec3 viewEye; +uniform vec3 viewCenter; +uniform float runTime; +uniform vec2 resolution; + +// The MIT License +// Copyright © 2013 Inigo Quilez +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// 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 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// A list of useful distance function to simple primitives, and an example on how to +// do some interesting boolean operations, repetition and displacement. +// +// More info here: http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm + +#define AA 1 // make this 1 if your machine is too slow + +//------------------------------------------------------------------ + +float sdPlane( vec3 p ) +{ + return p.y; +} + +float sdSphere( vec3 p, float s ) +{ + return length(p)-s; +} + +float sdBox( vec3 p, vec3 b ) +{ + vec3 d = abs(p) - b; + return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0)); +} + +float sdEllipsoid( in vec3 p, in vec3 r ) +{ + return (length( p/r ) - 1.0) * min(min(r.x,r.y),r.z); +} + +float udRoundBox( vec3 p, vec3 b, float r ) +{ + return length(max(abs(p)-b,0.0))-r; +} + +float sdTorus( vec3 p, vec2 t ) +{ + return length( vec2(length(p.xz)-t.x,p.y) )-t.y; +} + +float sdHexPrism( vec3 p, vec2 h ) +{ + vec3 q = abs(p); +#if 0 + return max(q.z-h.y,max((q.x*0.866025+q.y*0.5),q.y)-h.x); +#else + float d1 = q.z-h.y; + float d2 = max((q.x*0.866025+q.y*0.5),q.y)-h.x; + return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); +#endif +} + +float sdCapsule( vec3 p, vec3 a, vec3 b, float r ) +{ + vec3 pa = p-a, ba = b-a; + float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); + return length( pa - ba*h ) - r; +} + +float sdEquilateralTriangle( in vec2 p ) +{ + const float k = sqrt(3.0); + p.x = abs(p.x) - 1.0; + p.y = p.y + 1.0/k; + if( p.x + k*p.y > 0.0 ) p = vec2( p.x - k*p.y, -k*p.x - p.y )/2.0; + p.x += 2.0 - 2.0*clamp( (p.x+2.0)/2.0, 0.0, 1.0 ); + return -length(p)*sign(p.y); +} + +float sdTriPrism( vec3 p, vec2 h ) +{ + vec3 q = abs(p); + float d1 = q.z-h.y; +#if 1 + // distance bound + float d2 = max(q.x*0.866025+p.y*0.5,-p.y)-h.x*0.5; +#else + // correct distance + h.x *= 0.866025; + float d2 = sdEquilateralTriangle(p.xy/h.x)*h.x; +#endif + return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); +} + +float sdCylinder( vec3 p, vec2 h ) +{ + vec2 d = abs(vec2(length(p.xz),p.y)) - h; + return min(max(d.x,d.y),0.0) + length(max(d,0.0)); +} + +float sdCone( in vec3 p, in vec3 c ) +{ + vec2 q = vec2( length(p.xz), p.y ); + float d1 = -q.y-c.z; + float d2 = max( dot(q,c.xy), q.y); + return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); +} + +float sdConeSection( in vec3 p, in float h, in float r1, in float r2 ) +{ + float d1 = -p.y - h; + float q = p.y - h; + float si = 0.5*(r1-r2)/h; + float d2 = max( sqrt( dot(p.xz,p.xz)*(1.0-si*si)) + q*si - r2, q ); + return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); +} + +float sdPryamid4(vec3 p, vec3 h ) // h = { cos a, sin a, height } +{ + // Tetrahedron = Octahedron - Cube + float box = sdBox( p - vec3(0,-2.0*h.z,0), vec3(2.0*h.z) ); + + float d = 0.0; + d = max( d, abs( dot(p, vec3( -h.x, h.y, 0 )) )); + d = max( d, abs( dot(p, vec3( h.x, h.y, 0 )) )); + d = max( d, abs( dot(p, vec3( 0, h.y, h.x )) )); + d = max( d, abs( dot(p, vec3( 0, h.y,-h.x )) )); + float octa = d - h.z; + return max(-box,octa); // Subtraction + } + +float length2( vec2 p ) +{ + return sqrt( p.x*p.x + p.y*p.y ); +} + +float length6( vec2 p ) +{ + p = p*p*p; p = p*p; + return pow( p.x + p.y, 1.0/6.0 ); +} + +float length8( vec2 p ) +{ + p = p*p; p = p*p; p = p*p; + return pow( p.x + p.y, 1.0/8.0 ); +} + +float sdTorus82( vec3 p, vec2 t ) +{ + vec2 q = vec2(length2(p.xz)-t.x,p.y); + return length8(q)-t.y; +} + +float sdTorus88( vec3 p, vec2 t ) +{ + vec2 q = vec2(length8(p.xz)-t.x,p.y); + return length8(q)-t.y; +} + +float sdCylinder6( vec3 p, vec2 h ) +{ + return max( length6(p.xz)-h.x, abs(p.y)-h.y ); +} + +//------------------------------------------------------------------ + +float opS( float d1, float d2 ) +{ + return max(-d2,d1); +} + +vec2 opU( vec2 d1, vec2 d2 ) +{ + return (d1.x0.0 ) tmax = min( tmax, tp1 ); + float tp2 = (1.6-ro.y)/rd.y; if( tp2>0.0 ) { if( ro.y>1.6 ) tmin = max( tmin, tp2 ); + else tmax = min( tmax, tp2 ); } +#endif + + float t = tmin; + float m = -1.0; + for( int i=0; i<64; i++ ) + { + float precis = 0.0005*t; + vec2 res = map( ro+rd*t ); + if( res.xtmax ) break; + t += res.x; + m = res.y; + } + + if( t>tmax ) m=-1.0; + return vec2( t, m ); +} + + +float calcSoftshadow( in vec3 ro, in vec3 rd, in float mint, in float tmax ) +{ + float res = 1.0; + float t = mint; + for( int i=0; i<16; i++ ) + { + float h = map( ro + rd*t ).x; + res = min( res, 8.0*h/t ); + t += clamp( h, 0.02, 0.10 ); + if( h<0.001 || t>tmax ) break; + } + return clamp( res, 0.0, 1.0 ); +} + +vec3 calcNormal( in vec3 pos ) +{ + vec2 e = vec2(1.0,-1.0)*0.5773*0.0005; + return normalize( e.xyy*map( pos + e.xyy ).x + + e.yyx*map( pos + e.yyx ).x + + e.yxy*map( pos + e.yxy ).x + + e.xxx*map( pos + e.xxx ).x ); + /* + vec3 eps = vec3( 0.0005, 0.0, 0.0 ); + vec3 nor = vec3( + map(pos+eps.xyy).x - map(pos-eps.xyy).x, + map(pos+eps.yxy).x - map(pos-eps.yxy).x, + map(pos+eps.yyx).x - map(pos-eps.yyx).x ); + return normalize(nor); + */ +} + +float calcAO( in vec3 pos, in vec3 nor ) +{ + float occ = 0.0; + float sca = 1.0; + for( int i=0; i<5; i++ ) + { + float hr = 0.01 + 0.12*float(i)/4.0; + vec3 aopos = nor * hr + pos; + float dd = map( aopos ).x; + occ += -(dd-hr)*sca; + sca *= 0.95; + } + return clamp( 1.0 - 3.0*occ, 0.0, 1.0 ); +} + +// http://iquilezles.org/www/articles/checkerfiltering/checkerfiltering.htm +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; + // xor pattern + return 0.5 - 0.5*i.x*i.y; +} + +vec3 render( in vec3 ro, in vec3 rd ) +{ + vec3 col = vec3(0.7, 0.9, 1.0) +rd.y*0.8; + vec2 res = castRay(ro,rd); + float t = res.x; + float m = res.y; + if( m>-0.5 ) + { + vec3 pos = ro + t*rd; + vec3 nor = calcNormal( pos ); + vec3 ref = reflect( rd, nor ); + + // material + col = 0.45 + 0.35*sin( vec3(0.05,0.08,0.10)*(m-1.0) ); + if( m<1.5 ) + { + + float f = checkersGradBox( 5.0*pos.xz ); + col = 0.3 + f*vec3(0.1); + } + + // lighting + float occ = calcAO( pos, nor ); + vec3 lig = normalize( vec3(cos(-0.4 * runTime), sin(0.7 * runTime), -0.6) ); + vec3 hal = normalize( lig-rd ); + float amb = clamp( 0.5+0.5*nor.y, 0.0, 1.0 ); + float dif = clamp( dot( nor, lig ), 0.0, 1.0 ); + float bac = clamp( dot( nor, normalize(vec3(-lig.x,0.0,-lig.z))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0); + float dom = smoothstep( -0.1, 0.1, ref.y ); + float fre = pow( clamp(1.0+dot(nor,rd),0.0,1.0), 2.0 ); + + dif *= calcSoftshadow( pos, lig, 0.02, 2.5 ); + dom *= calcSoftshadow( pos, ref, 0.02, 2.5 ); + + float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0)* + dif * + (0.04 + 0.96*pow( clamp(1.0+dot(hal,rd),0.0,1.0), 5.0 )); + + vec3 lin = vec3(0.0); + lin += 1.30*dif*vec3(1.00,0.80,0.55); + lin += 0.40*amb*vec3(0.40,0.60,1.00)*occ; + lin += 0.50*dom*vec3(0.40,0.60,1.00)*occ; + lin += 0.50*bac*vec3(0.25,0.25,0.25)*occ; + lin += 0.25*fre*vec3(1.00,1.00,1.00)*occ; + col = col*lin; + col += 10.00*spe*vec3(1.00,0.90,0.70); + + col = mix( col, vec3(0.8,0.9,1.0), 1.0-exp( -0.0002*t*t*t ) ); + } + + return vec3( clamp(col,0.0,1.0) ); +} + +mat3 setCamera( in vec3 ro, in vec3 ta, float cr ) +{ + vec3 cw = normalize(ta-ro); + vec3 cp = vec3(sin(cr), cos(cr),0.0); + vec3 cu = normalize( cross(cw,cp) ); + vec3 cv = normalize( cross(cu,cw) ); + return mat3( cu, cv, cw ); +} + +void main() +{ + vec3 tot = vec3(0.0); +#if AA>1 + for( int m=0; m1 + } + tot /= float(AA*AA); +#endif + + gl_FragColor = vec4( tot, 1.0 ); +} From db8b1993632c909f7ad98138e54ebcc09d80e9f6 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 6 Aug 2024 18:32:14 +0200 Subject: [PATCH 13/47] Reviewed shader --- .../resources/shaders/glsl100/raymarching.fs | 2 +- .../resources/shaders/glsl120/raymarching.fs | 26 +------------------ 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl100/raymarching.fs b/examples/shaders/resources/shaders/glsl100/raymarching.fs index d58d9d3318ab..a7339d2166c7 100644 --- a/examples/shaders/resources/shaders/glsl100/raymarching.fs +++ b/examples/shaders/resources/shaders/glsl100/raymarching.fs @@ -38,7 +38,7 @@ uniform vec2 resolution; // // More info here: http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm -#define AA 1 // make this 1 is your machine is too slow +#define AA 1 // make this 1 if your machine is too slow //------------------------------------------------------------------ diff --git a/examples/shaders/resources/shaders/glsl120/raymarching.fs b/examples/shaders/resources/shaders/glsl120/raymarching.fs index d3180ac12a0d..efe4fa108282 100644 --- a/examples/shaders/resources/shaders/glsl120/raymarching.fs +++ b/examples/shaders/resources/shaders/glsl120/raymarching.fs @@ -1,32 +1,8 @@ -// The MIT License -// Copyright © 2013 Inigo Quilez -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// 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 -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// A list of useful distance function to simple primitives, and an example on how to -// do some interesting boolean operations, repetition and displacement. -// -// More info here: http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm - #version 120 // Input vertex attributes (from vertex shader) varying vec2 fragTexCoord; +varying vec4 fragColor; uniform vec3 viewEye; uniform vec3 viewCenter; From 5af331d708e0fb4d0a978893aa035e72488cf0e5 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 7 Aug 2024 01:01:45 +0200 Subject: [PATCH 14/47] REVIEWED #4206 --- src/rlgl.h | 2 +- src/rtextures.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 3ff6d7e2a750..8ee6026446bb 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -4993,7 +4993,7 @@ static int rlGetPixelDataSize(int width, int height, int format) default: break; } - float bytesPerPixel = (float)bpp/8.0f; + double bytesPerPixel = (double)bpp/8.0; dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes // Most compressed formats works on 4x4 blocks, diff --git a/src/rtextures.c b/src/rtextures.c index 9359f9219842..5fa01c9dff2d 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -5403,7 +5403,7 @@ int GetPixelDataSize(int width, int height, int format) default: break; } - float bytesPerPixel = (float)bpp/8.0f; + double bytesPerPixel = (double)bpp/8.0; dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes // Most compressed formats works on 4x4 blocks, From 97c02b2425225982da6f3569d50fb490fab53c10 Mon Sep 17 00:00:00 2001 From: kai-z99 <147789796+kai-z99@users.noreply.github.com> Date: Tue, 6 Aug 2024 16:05:53 -0700 Subject: [PATCH 15/47] [examples] Fix some examples (#4211) * Add text * small typo --- examples/shapes/shapes_draw_circle_sector.c | 8 ++++---- examples/shapes/shapes_draw_rectangle_rounded.c | 10 +++++----- examples/shapes/shapes_draw_ring.c | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/shapes/shapes_draw_circle_sector.c b/examples/shapes/shapes_draw_circle_sector.c index 30eaaf4bd1e6..cb9ab859b4a5 100644 --- a/examples/shapes/shapes_draw_circle_sector.c +++ b/examples/shapes/shapes_draw_circle_sector.c @@ -63,11 +63,11 @@ int main(void) // Draw GUI controls //------------------------------------------------------------------------------ - GuiSliderBar((Rectangle){ 600, 40, 120, 20}, "StartAngle", NULL, &startAngle, 0, 720); - GuiSliderBar((Rectangle){ 600, 70, 120, 20}, "EndAngle", NULL, &endAngle, 0, 720); + GuiSliderBar((Rectangle){ 600, 40, 120, 20}, "StartAngle", TextFormat("%.2f", startAngle), &startAngle, 0, 720); + GuiSliderBar((Rectangle){ 600, 70, 120, 20}, "EndAngle", TextFormat("%.2f", endAngle), &endAngle, 0, 720); - GuiSliderBar((Rectangle){ 600, 140, 120, 20}, "Radius", NULL, &outerRadius, 0, 200); - GuiSliderBar((Rectangle){ 600, 170, 120, 20}, "Segments", NULL, &segments, 0, 100); + GuiSliderBar((Rectangle){ 600, 140, 120, 20}, "Radius", TextFormat("%.2f", outerRadius), &outerRadius, 0, 200); + GuiSliderBar((Rectangle){ 600, 170, 120, 20}, "Segments", TextFormat("%.2f", segments), &segments, 0, 100); //------------------------------------------------------------------------------ minSegments = truncf(ceilf((endAngle - startAngle) / 90)); diff --git a/examples/shapes/shapes_draw_rectangle_rounded.c b/examples/shapes/shapes_draw_rectangle_rounded.c index 19280ed008f2..58991884c025 100644 --- a/examples/shapes/shapes_draw_rectangle_rounded.c +++ b/examples/shapes/shapes_draw_rectangle_rounded.c @@ -66,11 +66,11 @@ int main(void) // Draw GUI controls //------------------------------------------------------------------------------ - GuiSliderBar((Rectangle){ 640, 40, 105, 20 }, "Width", NULL, &width, 0, (float)GetScreenWidth() - 300); - GuiSliderBar((Rectangle){ 640, 70, 105, 20 }, "Height", NULL, &height, 0, (float)GetScreenHeight() - 50); - GuiSliderBar((Rectangle){ 640, 140, 105, 20 }, "Roundness", NULL, &roundness, 0.0f, 1.0f); - GuiSliderBar((Rectangle){ 640, 170, 105, 20 }, "Thickness", NULL, &lineThick, 0, 20); - GuiSliderBar((Rectangle){ 640, 240, 105, 20}, "Segments", NULL, &segments, 0, 60); + GuiSliderBar((Rectangle){ 640, 40, 105, 20 }, "Width", TextFormat("%.2f", width), &width, 0, (float)GetScreenWidth() - 300); + GuiSliderBar((Rectangle){ 640, 70, 105, 20 }, "Height", TextFormat("%.2f", height), &height, 0, (float)GetScreenHeight() - 50); + GuiSliderBar((Rectangle){ 640, 140, 105, 20 }, "Roundness", TextFormat("%.2f", roundness), &roundness, 0.0f, 1.0f); + GuiSliderBar((Rectangle){ 640, 170, 105, 20 }, "Thickness", TextFormat("%.2f", lineThick), &lineThick, 0, 20); + GuiSliderBar((Rectangle){ 640, 240, 105, 20}, "Segments", TextFormat("%.2f", segments), &segments, 0, 60); GuiCheckBox((Rectangle){ 640, 320, 20, 20 }, "DrawRoundedRect", &drawRoundedRect); GuiCheckBox((Rectangle){ 640, 350, 20, 20 }, "DrawRoundedLines", &drawRoundedLines); diff --git a/examples/shapes/shapes_draw_ring.c b/examples/shapes/shapes_draw_ring.c index 60c95a4c0e15..8bccdf729bc9 100644 --- a/examples/shapes/shapes_draw_ring.c +++ b/examples/shapes/shapes_draw_ring.c @@ -69,13 +69,13 @@ int main(void) // Draw GUI controls //------------------------------------------------------------------------------ - GuiSliderBar((Rectangle){ 600, 40, 120, 20 }, "StartAngle", NULL, &startAngle, -450, 450); - GuiSliderBar((Rectangle){ 600, 70, 120, 20 }, "EndAngle", NULL, &endAngle, -450, 450); + GuiSliderBar((Rectangle){ 600, 40, 120, 20 }, "StartAngle", TextFormat("%.2f", startAngle), &startAngle, -450, 450); + GuiSliderBar((Rectangle){ 600, 70, 120, 20 }, "EndAngle", TextFormat("%.2f", endAngle), &endAngle, -450, 450); - GuiSliderBar((Rectangle){ 600, 140, 120, 20 }, "InnerRadius", NULL, &innerRadius, 0, 100); - GuiSliderBar((Rectangle){ 600, 170, 120, 20 }, "OuterRadius", NULL, &outerRadius, 0, 200); + GuiSliderBar((Rectangle){ 600, 140, 120, 20 }, "InnerRadius", TextFormat("%.2f", innerRadius), &innerRadius, 0, 100); + GuiSliderBar((Rectangle){ 600, 170, 120, 20 }, "OuterRadius", TextFormat("%.2f", outerRadius), &outerRadius, 0, 200); - GuiSliderBar((Rectangle){ 600, 240, 120, 20 }, "Segments", NULL, &segments, 0, 100); + GuiSliderBar((Rectangle){ 600, 240, 120, 20 }, "Segments", TextFormat("%.2f", segments), &segments, 0, 100); GuiCheckBox((Rectangle){ 600, 320, 20, 20 }, "Draw Ring", &drawRing); GuiCheckBox((Rectangle){ 600, 350, 20, 20 }, "Draw RingLines", &drawRingLines); From cae0946764a7ef81e92decd5ae279a48ff22fd04 Mon Sep 17 00:00:00 2001 From: freakmangd <53349189+freakmangd@users.noreply.github.com> Date: Fri, 9 Aug 2024 02:53:29 -0400 Subject: [PATCH 16/47] Fix build.zig and use zig fmt (#4242) + `std.mem.split` is deprecated, `splitScalar` seems like the intended choice here + zig files should be formatted according to `zig fmt` --- src/build.zig | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/build.zig b/src/build.zig index 65379c03b4ab..4e112d78f01e 100644 --- a/src/build.zig +++ b/src/build.zig @@ -59,15 +59,15 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. const content = try std.fs.cwd().readFileAlloc(b.allocator, file, std.math.maxInt(usize)); defer b.allocator.free(content); - var lines = std.mem.split(u8, content, "\n"); + var lines = std.mem.splitScalar(u8, content, '\n'); while (lines.next()) |line| { if (!std.mem.containsAtLeast(u8, line, 1, "SUPPORT")) continue; if (std.mem.startsWith(u8, line, "//")) continue; - var flag = std.mem.trimLeft(u8, line, " \t"); // Trim whitespace - flag = flag["#define ".len - 1 ..]; // Remove #define - flag = std.mem.trimLeft(u8, flag, " \t"); // Trim whitespace - flag = flag[0..std.mem.indexOf(u8, flag, " ").?]; // Flag is only one word, so capture till space + var flag = std.mem.trimLeft(u8, line, " \t"); // Trim whitespace + flag = flag["#define ".len - 1 ..]; // Remove #define + flag = std.mem.trimLeft(u8, flag, " \t"); // Trim whitespace + flag = flag[0..std.mem.indexOf(u8, flag, " ").?]; // Flag is only one word, so capture till space flag = try std.fmt.allocPrint(b.allocator, "-D{s}", .{flag}); // Prepend with -D // If user specifies the flag skip it @@ -149,9 +149,8 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. raylib.addLibraryPath(.{ .cwd_relative = "/usr/lib" }); raylib.addIncludePath(.{ .cwd_relative = "/usr/include" }); if (options.linux_display_backend == .X11 or options.linux_display_backend == .Both) { - - raylib.defineCMacro("_GLFW_X11", null); - raylib.linkSystemLibrary("X11"); + raylib.defineCMacro("_GLFW_X11", null); + raylib.linkSystemLibrary("X11"); } if (options.linux_display_backend == .Wayland or options.linux_display_backend == .Both) { From 9ef678d90ae3973113eace62425508fe267bed5d Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Thu, 8 Aug 2024 23:54:22 -0700 Subject: [PATCH 17/47] Fix warnings (#4239) * Update raylib_api.* by CI * Fix typecast warnings --------- Co-authored-by: github-actions[bot] --- examples/audio/audio_mixed_processor.c | 2 +- src/rcore.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/audio/audio_mixed_processor.c b/examples/audio/audio_mixed_processor.c index 2df381b26552..df46edc4eaa9 100644 --- a/examples/audio/audio_mixed_processor.c +++ b/examples/audio/audio_mixed_processor.c @@ -97,7 +97,7 @@ int main(void) DrawRectangle(199, 199, 402, 34, LIGHTGRAY); for (int i = 0; i < 400; i++) { - DrawLine(201 + i, 232 - (averageVolume[i] * 32), 201 + i, 232, MAROON); + DrawLine(201 + i, 232 - (int)(averageVolume[i] * 32), 201 + i, 232, MAROON); } DrawRectangleLines(199, 199, 402, 34, GRAY); diff --git a/src/rcore.c b/src/rcore.c index b4bc19f344d4..93ae4f3bb598 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2955,7 +2955,7 @@ float GetGamepadAxisMovement(int gamepad, int axis) float value = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f; if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS)) { - float movement = value < 0.0f ? CORE.Input.Gamepad.axisState[gamepad][axis] : fabs(CORE.Input.Gamepad.axisState[gamepad][axis]); + float movement = value < 0.0f ? CORE.Input.Gamepad.axisState[gamepad][axis] : fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]); // 0.1f = GAMEPAD_AXIS_MINIMUM_DRIFT/DELTA if (movement > value + 0.1f) value = CORE.Input.Gamepad.axisState[gamepad][axis]; From 5233b80fb73c557c6836153c4db525c1ffe6c36f Mon Sep 17 00:00:00 2001 From: Anthony Carbajal <5776225+CrackedPixel@users.noreply.github.com> Date: Fri, 9 Aug 2024 01:55:16 -0500 Subject: [PATCH 18/47] update raygui (#4238) --- examples/shapes/raygui.h | 670 +++++++++++++++++++++++++-------------- 1 file changed, 434 insertions(+), 236 deletions(-) diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index 623115fcec0d..16e01a28f423 100644 --- a/examples/shapes/raygui.h +++ b/examples/shapes/raygui.h @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raygui v4.0 - A simple and easy-to-use immediate-mode gui library +* raygui v4.5-dev - A simple and easy-to-use immediate-mode gui library * * DESCRIPTION: * raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also @@ -26,7 +26,7 @@ * NOTES: * - 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 GuiLoadStyleDefaulf() unloads +* no unload function is explicitly provided... but note that GuiLoadStyleDefault() unloads * by default any previously loaded font (texture, recs, glyphs). * - Global UI alpha (guiAlpha) is applied inside GuiDrawRectangle() and GuiDrawText() functions * @@ -141,6 +141,24 @@ * Draw text bounds rectangles for debug * * VERSIONS HISTORY: +* 4.5-dev (Sep-2024) Current dev version... +* 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: Multiple new icons +* REVIEWED: GuiTabBar(), close tab with mouse middle button +* REVIEWED: GuiScrollPanel(), scroll speed proportional to content +* REVIEWED: GuiDropdownBox(), support roll up and hidden arrow +* REVIEWED: GuiTextBox(), cursor position initialization +* REVIEWED: GuiSliderPro(), control value change check +* REVIEWED: GuiGrid(), simplified implementation +* REVIEWED: GuiIconText(), increase buffer size and reviewed padding +* REVIEWED: GuiDrawText(), improved wrap mode drawing +* REVIEWED: GuiScrollBar(), minor tweaks +* REVIEWED: Functions descriptions, removed wrong return value reference +* REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion +* * 4.0 (12-Sep-2023) ADDED: GuiToggleSlider() * ADDED: GuiColorPickerHSV() and GuiColorPanelHSV() * ADDED: Multiple new icons, mostly compiler related @@ -314,9 +332,9 @@ #define RAYGUI_H #define RAYGUI_VERSION_MAJOR 4 -#define RAYGUI_VERSION_MINOR 0 +#define RAYGUI_VERSION_MINOR 5 #define RAYGUI_VERSION_PATCH 0 -#define RAYGUI_VERSION "4.0" +#define RAYGUI_VERSION "4.5-dev" #if !defined(RAYGUI_STANDALONE) #include "raylib.h" @@ -441,7 +459,6 @@ } Font; #endif - // Style property // NOTE: Used when exporting style as code for convenience typedef struct GuiStyleProp { @@ -546,7 +563,6 @@ typedef enum { // TEXT_SIZE, TEXT_SPACING, TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE are global and // should be configured by user as needed while defining the UI layout - // Gui extended properties depend on control // NOTE: RAYGUI_MAX_PROPS_EXTENDED properties (by default, max 8 properties) //---------------------------------------------------------------------------------- @@ -562,12 +578,12 @@ typedef enum { TEXT_ALIGNMENT_VERTICAL, // Text vertical alignment inside text bounds (after border and padding) TEXT_WRAP_MODE // Text wrap-mode inside text bounds //TEXT_DECORATION // Text decoration: 0-None, 1-Underline, 2-Line-through, 3-Overline - //TEXT_DECORATION_THICK // Text decoration line thikness + //TEXT_DECORATION_THICK // Text decoration line thickness } GuiDefaultProperty; // Other possible text properties: // TEXT_WEIGHT // Normal, Italic, Bold -> Requires specific font change -// TEXT_INDENT // Text indentation -> Now using TEXT_PADDING... +// TEXT_INDENT // Text indentation -> Now using TEXT_PADDING... // Label //typedef enum { } GuiLabelProperty; @@ -615,7 +631,9 @@ typedef enum { // DropdownBox typedef enum { ARROW_PADDING = 16, // DropdownBox arrow separation from border and items - DROPDOWN_ITEMS_SPACING // DropdownBox items separation + DROPDOWN_ITEMS_SPACING, // DropdownBox items separation + DROPDOWN_ARROW_HIDDEN, // DropdownBox arrow hidden + DROPDOWN_ROLL_UP // DropdownBox roll up flag (default rolls down) } GuiDropdownBoxProperty; // TextBox/TextBoxMulti/ValueBox/Spinner @@ -635,6 +653,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_WIDTH // ListView items border width } GuiListViewProperty; // ColorPicker @@ -698,7 +717,6 @@ 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 - // Controls //---------------------------------------------------------------------------------------------------------- // Container/separator controls, useful for controls organization @@ -710,29 +728,30 @@ RAYGUIAPI int GuiTabBar(Rectangle bounds, const char **text, int count, int *act RAYGUIAPI int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view); // Scroll Panel control // Basic controls set -RAYGUIAPI int GuiLabel(Rectangle bounds, const char *text); // Label control, shows text +RAYGUIAPI int GuiLabel(Rectangle bounds, const char *text); // Label control RAYGUIAPI int GuiButton(Rectangle bounds, const char *text); // Button control, returns true when clicked -RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text); // Label button control, show true when clicked -RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active); // Toggle Button control, returns true when active -RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active); // Toggle Group control, returns active toggle index -RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active); // Toggle Slider control, returns true when clicked +RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text); // Label button control, returns true when clicked +RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active); // Toggle Button control +RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active); // Toggle Group control +RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active); // Toggle Slider control RAYGUIAPI int GuiCheckBox(Rectangle bounds, const char *text, bool *checked); // Check Box control, returns true when active -RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int *active); // Combo Box control, returns selected item index +RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int *active); // Combo Box control -RAYGUIAPI int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control, returns selected item -RAYGUIAPI int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control, returns selected value +RAYGUIAPI int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control +RAYGUIAPI int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control RAYGUIAPI int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers +RAYGUIAPI int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode); // Value box control for float values RAYGUIAPI int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control, updates input text -RAYGUIAPI int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider control, returns selected value -RAYGUIAPI int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider Bar control, returns selected value -RAYGUIAPI int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Progress Bar control, shows current progress value +RAYGUIAPI int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider control +RAYGUIAPI int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider Bar control +RAYGUIAPI int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Progress Bar control RAYGUIAPI int GuiStatusBar(Rectangle bounds, const char *text); // Status Bar control, shows info text RAYGUIAPI int GuiDummyRec(Rectangle bounds, const char *text); // Dummy control for placeholders -RAYGUIAPI int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell); // Grid control, returns mouse cell position +RAYGUIAPI int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell); // Grid control // Advance controls set -RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active); // List View control, returns selected list item index +RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active); // List View control RAYGUIAPI int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters RAYGUIAPI int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons); // Message Box control, displays a message RAYGUIAPI int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive); // Text Input Box control, ask for text, supports secret @@ -741,10 +760,9 @@ RAYGUIAPI int GuiColorPanel(Rectangle bounds, const char *text, Color *color); RAYGUIAPI int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha); // Color Bar Alpha control RAYGUIAPI int GuiColorBarHue(Rectangle bounds, const char *text, float *value); // Color Bar Hue control RAYGUIAPI int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv); // Color Picker control that avoids conversion to RGB on each call (multiple color controls) -RAYGUIAPI int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv); // Color Panel control that returns HSV color value, used by GuiColorPickerHSV() +RAYGUIAPI int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv); // Color Panel control that updates Hue-Saturation-Value color value, used by GuiColorPickerHSV() //---------------------------------------------------------------------------------------------------------- - #if !defined(RAYGUI_NO_ICONS) #if !defined(RAYGUI_CUSTOM_ICONS) @@ -972,14 +990,14 @@ typedef enum { ICON_FOLDER = 217, ICON_FILE = 218, ICON_SAND_TIMER = 219, - ICON_220 = 220, - ICON_221 = 221, - ICON_222 = 222, - ICON_223 = 223, - ICON_224 = 224, - ICON_225 = 225, - ICON_226 = 226, - ICON_227 = 227, + ICON_WARNING = 220, + ICON_HELP_BOX = 221, + ICON_INFO_BOX = 222, + ICON_PRIORITY = 223, + ICON_LAYERS_ISO = 224, + ICON_LAYERS2 = 225, + ICON_MLAYERS = 226, + ICON_MAPS = 227, ICON_228 = 228, ICON_229 = 229, ICON_230 = 230, @@ -1290,14 +1308,14 @@ static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] = 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // ICON_FOLDER 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x00003ffc, // ICON_FILE 0x1ff00000, 0x20082008, 0x17d02fe8, 0x05400ba0, 0x09200540, 0x23881010, 0x2fe827c8, 0x00001ff0, // ICON_SAND_TIMER - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_220 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_221 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_222 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_223 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_224 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_225 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_226 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_227 + 0x01800000, 0x02400240, 0x05a00420, 0x09900990, 0x11881188, 0x21842004, 0x40024182, 0x00003ffc, // ICON_WARNING + 0x7ffe0000, 0x4ff24002, 0x4c324ff2, 0x4f824c02, 0x41824f82, 0x41824002, 0x40024182, 0x00007ffe, // ICON_HELP_BOX + 0x7ffe0000, 0x41824002, 0x40024182, 0x41824182, 0x41824182, 0x41824182, 0x40024182, 0x00007ffe, // ICON_INFO_BOX + 0x01800000, 0x04200240, 0x10080810, 0x7bde2004, 0x0a500a50, 0x08500bd0, 0x08100850, 0x00000ff0, // ICON_PRIORITY + 0x01800000, 0x18180660, 0x80016006, 0x98196006, 0x99996666, 0x19986666, 0x01800660, 0x00000000, // ICON_LAYERS_ISO + 0x07fe0000, 0x1c020402, 0x74021402, 0x54025402, 0x54025402, 0x500857fe, 0x40205ff8, 0x00007fe0, // ICON_LAYERS2 + 0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x422a422a, 0x422e422a, 0x40384e28, 0x00007fe0, // ICON_MLAYERS + 0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x5b2a512a, 0x512e552a, 0x40385128, 0x00007fe0, // ICON_MAPS 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_228 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_229 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_230 @@ -1328,7 +1346,7 @@ static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] = 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_255 }; -// NOTE: We keep a pointer to the icons array, useful to point to other sets if required +// NOTE: A pointer to current icons array should be defined static unsigned int *guiIconsPtr = guiIcons; #endif // !RAYGUI_NO_ICONS && !RAYGUI_CUSTOM_ICONS @@ -1363,8 +1381,8 @@ static unsigned int guiIconScale = 1; // Gui icon default scale (if ic static bool guiTooltip = false; // Tooltip enabled/disabled static const char *guiTooltipPtr = NULL; // Tooltip string pointer (string provided by user) -static bool guiSliderDragging = false; // Gui slider drag state (no inputs processed except dragged slider) -static Rectangle guiSliderActive = { 0 }; // Gui slider active bounds rectangle, used as an unique identifier +static bool guiControlExclusiveMode = false; // Gui control exclusive mode (no inputs processed except current control) +static Rectangle guiControlExclusiveRec = { 0 }; // Gui control exclusive bounds rectangle, used as an unique identifier static int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*() //static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking @@ -1450,6 +1468,7 @@ static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if static const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' static const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings static int TextToInteger(const char *text); // Get integer value from text +static float TextToFloat(const char *text); // Get float value from text static int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded text static const char *CodepointToUTF8(int codepoint, int *byteSize); // Encode codepoint into UTF-8 text (char array size returned as parameter) @@ -1744,6 +1763,9 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) if (toggle) *active = i; } + // Close tab with middle mouse button pressed + if (CheckCollisionPointRec(GetMousePosition(), tabBounds) && IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) result = i; + GuiSetStyle(TOGGLE, TEXT_PADDING, textPadding); GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment); @@ -1775,10 +1797,10 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector { #define RAYGUI_MIN_SCROLLBAR_WIDTH 40 #define RAYGUI_MIN_SCROLLBAR_HEIGHT 40 + #define RAYGUI_MIN_MOUSE_WHEEL_SPEED 20 int result = 0; GuiState state = guiState; - float mouseWheelSpeed = 20.0f; // Default movement speed with mouse wheel Rectangle temp = { 0 }; if (view == NULL) view = &temp; @@ -1820,17 +1842,8 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector }; // Make sure scroll bars have a minimum width/height - // NOTE: If content >>> bounds, size could be very small or even 0 - if (horizontalScrollBar.width < RAYGUI_MIN_SCROLLBAR_WIDTH) - { - horizontalScrollBar.width = RAYGUI_MIN_SCROLLBAR_WIDTH; - mouseWheelSpeed = 30.0f; // TODO: Calculate speed increment based on content.height vs bounds.height - } - if (verticalScrollBar.height < RAYGUI_MIN_SCROLLBAR_HEIGHT) - { - verticalScrollBar.height = RAYGUI_MIN_SCROLLBAR_HEIGHT; - mouseWheelSpeed = 30.0f; // TODO: Calculate speed increment based on content.width vs bounds.width - } + if (horizontalScrollBar.width < RAYGUI_MIN_SCROLLBAR_WIDTH) horizontalScrollBar.width = RAYGUI_MIN_SCROLLBAR_WIDTH; + if (verticalScrollBar.height < RAYGUI_MIN_SCROLLBAR_HEIGHT) verticalScrollBar.height = RAYGUI_MIN_SCROLLBAR_HEIGHT; // Calculate view area (area without the scrollbars) *view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? @@ -1873,9 +1886,14 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector #endif float wheelMove = GetMouseWheelMove(); + // Set scrolling speed with mouse wheel based on ratio between bounds and content + Vector2 mouseWheelSpeed = { content.width/bounds.width, content.height/bounds.height }; + if (mouseWheelSpeed.x < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.x = RAYGUI_MIN_MOUSE_WHEEL_SPEED; + if (mouseWheelSpeed.y < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.y = RAYGUI_MIN_MOUSE_WHEEL_SPEED; + // Horizontal and vertical scrolling with mouse wheel - if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*mouseWheelSpeed; - else scrollPos.y += wheelMove*mouseWheelSpeed; // Vertical scroll + if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*mouseWheelSpeed.x; + else scrollPos.y += wheelMove*mouseWheelSpeed.y; // Vertical scroll } } @@ -1921,7 +1939,7 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector } // Draw scrollbar lines depending on current state - GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), BLANK); + GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), BLANK); // Set scrollbar slider size back to the way it was before GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, slider); @@ -1959,7 +1977,7 @@ int GuiButton(Rectangle bounds, const char *text) // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { Vector2 mousePoint = GetMousePosition(); @@ -1997,7 +2015,7 @@ int GuiLabelButton(Rectangle bounds, const char *text) // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { Vector2 mousePoint = GetMousePosition(); @@ -2020,7 +2038,7 @@ int GuiLabelButton(Rectangle bounds, const char *text) return pressed; } -// Toggle Button control, returns true when active +// Toggle Button control int GuiToggle(Rectangle bounds, const char *text, bool *active) { int result = 0; @@ -2031,7 +2049,7 @@ int GuiToggle(Rectangle bounds, const char *text, bool *active) // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { Vector2 mousePoint = GetMousePosition(); @@ -2068,7 +2086,7 @@ int GuiToggle(Rectangle bounds, const char *text, bool *active) return result; } -// Toggle Group control, returns toggled button codepointIndex +// Toggle Group control int GuiToggleGroup(Rectangle bounds, const char *text, int *active) { #if !defined(RAYGUI_TOGGLEGROUP_MAX_ITEMS) @@ -2117,7 +2135,7 @@ int GuiToggleGroup(Rectangle bounds, const char *text, int *active) return result; } -// Toggle Slider control extended, returns true when clicked +// Toggle Slider control extended int GuiToggleSlider(Rectangle bounds, const char *text, int *active) { int result = 0; @@ -2211,7 +2229,7 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { Vector2 mousePoint = GetMousePosition(); @@ -2256,7 +2274,7 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) return result; } -// Combo Box control, returns selected item codepointIndex +// Combo Box control int GuiComboBox(Rectangle bounds, const char *text, int *active) { int result = 0; @@ -2279,7 +2297,7 @@ int GuiComboBox(Rectangle bounds, const char *text, int *active) // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiSliderDragging) + if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiControlExclusiveMode) { Vector2 mousePoint = GetMousePosition(); @@ -2327,21 +2345,28 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod int result = 0; GuiState state = guiState; + int temp = 0; + if (active == NULL) active = &temp; + int itemSelected = *active; int itemFocused = -1; + int direction = 0; // Dropdown box open direction: down (default) + if (GuiGetStyle(DROPDOWNBOX, DROPDOWN_ROLL_UP) == 1) direction = 1; // Up + // Get substrings items from text (items pointers, lengths and count) int itemCount = 0; const char **items = GuiTextSplit(text, ';', &itemCount, NULL); Rectangle boundsOpen = bounds; boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + if (direction == 1) boundsOpen.y -= itemCount*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)) + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING); Rectangle itemBounds = bounds; // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiSliderDragging) + if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiControlExclusiveMode) { Vector2 mousePoint = GetMousePosition(); @@ -2362,7 +2387,8 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod for (int i = 0; i < itemCount; i++) { // Update item rectangle y position for next item - itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); if (CheckCollisionPointRec(mousePoint, itemBounds)) { @@ -2406,7 +2432,8 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod for (int i = 0; i < itemCount; i++) { // Update item rectangle y position for next item - itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); if (i == itemSelected) { @@ -2422,14 +2449,17 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod } } - // Draw arrows (using icon if available) + if (!GuiGetStyle(DROPDOWNBOX, DROPDOWN_ARROW_HIDDEN)) + { + // Draw arrows (using icon if available) #if defined(RAYGUI_NO_ICONS) - GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 }, - TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); + GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); #else - GuiDrawText("#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 }, - TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); // ICON_ARROW_DOWN_FILL + GuiDrawText(direction? "#121#" : "#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); // ICON_ARROW_DOWN_FILL #endif + } //-------------------------------------------------------------------- *active = itemSelected; @@ -2440,7 +2470,7 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod // Text Box control // NOTE: Returns true on ENTER pressed (useful for data validation) -int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) +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 @@ -2456,7 +2486,10 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); - int textWidth = GetTextWidth(text) - GetTextWidth(text + textBoxCursorIndex); + int textLength = (int)strlen(text); // Get current text length + int thisCursorIndex = textBoxCursorIndex; + if (thisCursorIndex > textLength) thisCursorIndex = textLength; + int textWidth = GetTextWidth(text) - GetTextWidth(text + thisCursorIndex); int textIndexOffset = 0; // Text index offset to start drawing in the box // Cursor rectangle @@ -2496,7 +2529,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) if ((state != STATE_DISABLED) && // Control not disabled !GuiGetStyle(TEXTBOX, TEXT_READONLY) && // TextBox not on read-only mode !guiLocked && // Gui not locked - !guiSliderDragging && // No gui slider on dragging + !guiControlExclusiveMode && // No gui slider on dragging (wrapMode == TEXT_WRAP_NONE)) // No wrap mode { Vector2 mousePosition = GetMousePosition(); @@ -2505,6 +2538,8 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) { state = STATE_PRESSED; + if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength; + // If text does not fit in the textbox and current cursor position is out of bounds, // we add an index offset to text for drawing only what requires depending on cursor while (textWidth >= textBounds.width) @@ -2517,19 +2552,16 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) textWidth = GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex); } - int textLength = (int)strlen(text); // Get current text length int codepoint = GetCharPressed(); // Get Unicode codepoint if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n'; - if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength; - // Encode codepoint as UTF-8 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) < bufferSize)) + if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize)) { // Move forward data from cursor position for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize]; @@ -2564,6 +2596,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) 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'; @@ -2583,6 +2616,8 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) // Move backward text from cursor position for (int i = (textBoxCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize]; + // TODO Check: >= cursor+codepointsize and <= length-codepointsize + // Prevent cursor index from decrementing past 0 if (textBoxCursorIndex > 0) { @@ -2653,7 +2688,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2)) { mouseCursor.x = textBounds.x + textEndWidth; - mouseCursorIndex = (int)strlen(text); + mouseCursorIndex = textLength; } // Place cursor at required index on mouse click @@ -2685,7 +2720,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { - textBoxCursorIndex = (int)strlen(text); // GLOBAL: Place cursor index to the end of current text + textBoxCursorIndex = textLength; // GLOBAL: Place cursor index to the end of current text result = 1; } } @@ -2727,7 +2762,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) /* // Text Box control with multiple lines and word-wrap // NOTE: This text-box is readonly, no editing supported by default -bool GuiTextBoxMulti(Rectangle bounds, char *text, int bufferSize, bool editMode) +bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode) { bool pressed = false; @@ -2736,7 +2771,7 @@ bool GuiTextBoxMulti(Rectangle bounds, char *text, int bufferSize, bool editMode GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP); // TODO: Implement methods to calculate cursor position properly - pressed = GuiTextBox(bounds, text, bufferSize, editMode); + pressed = GuiTextBox(bounds, text, textSize, editMode); GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE); GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_NONE); @@ -2771,7 +2806,7 @@ int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { Vector2 mousePoint = GetMousePosition(); @@ -2846,7 +2881,7 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { Vector2 mousePoint = GetMousePosition(); @@ -2890,7 +2925,13 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in //if (*value > maxValue) *value = maxValue; //else if (*value < minValue) *value = minValue; - if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1; + if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + { + if (*value > maxValue) *value = maxValue; + else if (*value < minValue) *value = minValue; + + result = 1; + } } else { @@ -2930,55 +2971,152 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in return result; } +// Floating point Value Box control, updates input val_str with numbers +// NOTE: Requires static variables: frameCounter +int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode) +{ + #if !defined(RAYGUI_VALUEBOX_MAX_CHARS) + #define RAYGUI_VALUEBOX_MAX_CHARS 32 + #endif + + int result = 0; + GuiState state = guiState; + + //char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0"; + //sprintf(textValue, "%2.2f", *value); + + Rectangle textBounds = {0}; + if (text != NULL) + { + textBounds.width = (float)GetTextWidth(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; + if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GetMousePosition(); + + bool valueHasChanged = false; + + if (editMode) + { + state = STATE_PRESSED; + + int keyCount = (int)strlen(textValue); + + // Only allow keys in range [48..57] + if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) + { + if (GetTextWidth(textValue) < bounds.width) + { + int key = GetCharPressed(); + if (((key >= 48) && (key <= 57)) || + (key == '.') || + ((keyCount == 0) && (key == '+')) || // NOTE: Sign can only be in first position + ((keyCount == 0) && (key == '-'))) + { + textValue[keyCount] = (char)key; + keyCount++; + + valueHasChanged = true; + } + } + } + + // Pressed backspace + if (IsKeyPressed(KEY_BACKSPACE)) + { + if (keyCount > 0) + { + keyCount--; + textValue[keyCount] = '\0'; + valueHasChanged = true; + } + } + + if (valueHasChanged) *value = TextToFloat(textValue); + + if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1; + } + else + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = STATE_FOCUSED; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + Color baseColor = BLANK; + if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED)); + else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED)); + + 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 + 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)}; + GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED))); + } + + // Draw text label if provided + GuiDrawText(text, textBounds, + (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, + GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return result; +} + // 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 result = 0; - float oldValue = *value; GuiState state = guiState; float temp = (maxValue - minValue)/2.0f; if (value == NULL) value = &temp; - - int sliderValue = (int)(((*value - minValue)/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))); + float oldValue = *value; 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) }; - if (sliderWidth > 0) // Slider - { - slider.x += (sliderValue - sliderWidth/2); - slider.width = (float)sliderWidth; - } - else if (sliderWidth == 0) // SliderBar - { - slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH); - slider.width = (float)sliderValue; - } - // Update control //-------------------------------------------------------------------- if ((state != STATE_DISABLED) && !guiLocked) { Vector2 mousePoint = GetMousePosition(); - if (guiSliderDragging) // Keep dragging outside of bounds + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { - if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) { state = STATE_PRESSED; - // Get equivalent value and slider position from mousePosition.x - *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; + *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width-sliderWidth)) + minValue; } } else { - guiSliderDragging = false; - guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; } } else if (CheckCollisionPointRec(mousePoint, bounds)) @@ -2986,16 +3124,13 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { state = STATE_PRESSED; - guiSliderDragging = true; - guiSliderActive = bounds; // Store bounds as an identifier when dragging starts + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts if (!CheckCollisionPointRec(mousePoint, slider)) { // Get equivalent value and slider position from mousePosition.x - *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; - - if (sliderWidth > 0) slider.x = mousePoint.x - slider.width/2; // Slider - else if (sliderWidth == 0) slider.width = (float)sliderValue; // SliderBar + *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width-sliderWidth)) + minValue; } } else state = STATE_FOCUSED; @@ -3006,17 +3141,22 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, } // Control value change check - if(oldValue == *value) result = 0; + if (oldValue == *value) result = 0; else result = 1; - // Bar limits check + // Slider bar limits check + float sliderValue = (((*value - minValue)/(maxValue - minValue))*(bounds.width - sliderWidth - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))); if (sliderWidth > 0) // Slider { + slider.x += sliderValue; + slider.width = (float)sliderWidth; if (slider.x <= (bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH); else if ((slider.x + slider.width) >= (bounds.x + bounds.width)) slider.x = bounds.x + bounds.width - slider.width - GuiGetStyle(SLIDER, BORDER_WIDTH); } else if (sliderWidth == 0) // SliderBar { + slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH); + slider.width = sliderValue; if (slider.width > bounds.width) slider.width = bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH); } //-------------------------------------------------------------------- @@ -3171,7 +3311,7 @@ int GuiDummyRec(Rectangle bounds, const char *text) // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { Vector2 mousePoint = GetMousePosition(); @@ -3238,7 +3378,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { Vector2 mousePoint = GetMousePosition(); @@ -3291,6 +3431,8 @@ 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 (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))); @@ -3353,83 +3495,32 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd return result; } -// Color Panel control +// Color Panel control - Color (RGBA) variant. int GuiColorPanel(Rectangle bounds, const char *text, Color *color) { int result = 0; - GuiState state = guiState; - Vector2 pickerSelector = { 0 }; - - const Color colWhite = { 255, 255, 255, 255 }; - const Color colBlack = { 0, 0, 0, 255 }; 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. - pickerSelector.x = bounds.x + (float)hsv.y*bounds.width; // HSV: Saturation - pickerSelector.y = bounds.y + (1.0f - (float)hsv.z)*bounds.height; // HSV: Value - - Vector3 maxHue = { hsv.x, 1.0f, 1.0f }; - Vector3 rgbHue = ConvertHSVtoRGB(maxHue); - Color maxHueCol = { (unsigned char)(255.0f*rgbHue.x), - (unsigned char)(255.0f*rgbHue.y), - (unsigned char)(255.0f*rgbHue.z), 255 }; + GuiColorPanelHSV(bounds, text, &hsv); - // Update control - //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + // 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) { - Vector2 mousePoint = GetMousePosition(); + Vector3 rgb = ConvertHSVtoRGB(hsv); - if (CheckCollisionPointRec(mousePoint, bounds)) - { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) - { - state = STATE_PRESSED; - pickerSelector = mousePoint; - - // Calculate color from picker - Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y }; - - colorPick.x /= (float)bounds.width; // Get normalized value on x - colorPick.y /= (float)bounds.height; // Get normalized value on y - - hsv.y = colorPick.x; - hsv.z = 1.0f - colorPick.y; - - Vector3 rgb = ConvertHSVtoRGB(hsv); - - // NOTE: Vector3ToColor() only available on raylib 1.8.1 - *color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x), - (unsigned char)(255.0f*rgb.y), - (unsigned char)(255.0f*rgb.z), - (unsigned char)(255.0f*(float)color->a/255.0f) }; - - } - else state = STATE_FOCUSED; - } + // NOTE: Vector3ToColor() only available on raylib 1.8.1 + *color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x), + (unsigned char)(255.0f*rgb.y), + (unsigned char)(255.0f*rgb.z), + color->a }; } - //-------------------------------------------------------------------- - - // Draw control - //-------------------------------------------------------------------- - if (state != STATE_DISABLED) - { - DrawRectangleGradientEx(bounds, Fade(colWhite, guiAlpha), Fade(colWhite, guiAlpha), Fade(maxHueCol, guiAlpha), Fade(maxHueCol, guiAlpha)); - DrawRectangleGradientEx(bounds, Fade(colBlack, 0), Fade(colBlack, guiAlpha), Fade(colBlack, guiAlpha), Fade(colBlack, 0)); - - // Draw color picker: selector - Rectangle selector = { pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE) }; - GuiDrawRectangle(selector, 0, BLANK, colWhite); - } - else - { - DrawRectangleGradientEx(bounds, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.6f), guiAlpha)); - } - - GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); - //-------------------------------------------------------------------- - return result; } @@ -3451,11 +3542,11 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) { Vector2 mousePoint = GetMousePosition(); - if (guiSliderDragging) // Keep dragging outside of bounds + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { - if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) { state = STATE_PRESSED; @@ -3466,8 +3557,8 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) } else { - guiSliderDragging = false; - guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; } } else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) @@ -3475,8 +3566,8 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { state = STATE_PRESSED; - guiSliderDragging = true; - guiSliderActive = bounds; // Store bounds as an identifier when dragging starts + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts *alpha = (mousePoint.x - bounds.x)/bounds.width; if (*alpha <= 0.0f) *alpha = 0.0f; @@ -3537,11 +3628,11 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) { Vector2 mousePoint = GetMousePosition(); - if (guiSliderDragging) // Keep dragging outside of bounds + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { - if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) { state = STATE_PRESSED; @@ -3552,8 +3643,8 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) } else { - guiSliderDragging = false; - guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; } } else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) @@ -3561,8 +3652,8 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { state = STATE_PRESSED; - guiSliderDragging = true; - guiSliderActive = bounds; // Store bounds as an identifier when dragging starts + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts *hue = (mousePoint.y - bounds.y)*360/bounds.height; if (*hue <= 0.0f) *hue = 0.0f; @@ -3615,6 +3706,7 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) // float GuiColorBarAlpha(Rectangle bounds, float alpha) // float GuiColorBarHue(Rectangle bounds, float value) // NOTE: bounds define GuiColorPanel() size +// NOTE: this picker converts RGB to HSV, which can cause the Hue control to jump. If you have this problem, consider using the HSV variant instead int GuiColorPicker(Rectangle bounds, const char *text, Color *color) { int result = 0; @@ -3627,6 +3719,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. Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ (*color).r/255.0f, (*color).g/255.0f, (*color).b/255.0f }); GuiColorBarHue(boundsHue, NULL, &hsv.x); @@ -3668,8 +3761,7 @@ int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) return result; } -// Color Panel control, returns HSV color value in *colorHsv. -// Used by GuiColorPickerHSV() +// Color Panel control - HSV variant int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) { int result = 0; @@ -3690,15 +3782,47 @@ int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + if ((state != STATE_DISABLED) && !guiLocked) { Vector2 mousePoint = GetMousePosition(); - if (CheckCollisionPointRec(mousePoint, bounds)) + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) + { + pickerSelector = mousePoint; + + if (pickerSelector.x < bounds.x) pickerSelector.x = bounds.x; + if (pickerSelector.x > bounds.x + bounds.width) pickerSelector.x = bounds.x + bounds.width; + if (pickerSelector.y < bounds.y) pickerSelector.y = bounds.y; + if (pickerSelector.y > bounds.y + bounds.height) pickerSelector.y = bounds.y + bounds.height; + + // Calculate color from picker + Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y }; + + colorPick.x /= (float)bounds.width; // Get normalized value on x + colorPick.y /= (float)bounds.height; // Get normalized value on y + + colorHsv->y = colorPick.x; + colorHsv->z = 1.0f - colorPick.y; + + } + } + else + { + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds)) { if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { state = STATE_PRESSED; + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; pickerSelector = mousePoint; // Calculate color from picker @@ -3760,9 +3884,9 @@ int GuiMessageBox(Rectangle bounds, const char *title, const char *message, cons int textWidth = GetTextWidth(message) + 2; Rectangle textBounds = { 0 }; - textBounds.x = bounds.x + bounds.width/2 - textWidth/2; + textBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING; textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + RAYGUI_MESSAGEBOX_BUTTON_PADDING; - textBounds.width = (float)textWidth; + textBounds.width = bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*2; textBounds.height = bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 3*RAYGUI_MESSAGEBOX_BUTTON_PADDING - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT; // Draw control @@ -3905,7 +4029,7 @@ int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vect // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) { if (CheckCollisionPointRec(mousePoint, bounds)) { @@ -3925,14 +4049,14 @@ int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vect // Draw vertical grid lines for (int i = 0; i < linesV; i++) { - Rectangle lineV = { bounds.x + spacing*i/subdivs, bounds.y, 1, bounds.height }; + Rectangle lineV = { bounds.x + spacing*i/subdivs, bounds.y, 1, bounds.height + 1 }; GuiDrawRectangle(lineV, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA)); } // Draw horizontal grid lines for (int i = 0; i < linesH; i++) { - Rectangle lineH = { bounds.x, bounds.y + spacing*i/subdivs, bounds.width, 1 }; + Rectangle lineH = { bounds.x, bounds.y + spacing*i/subdivs, bounds.width + 1, 1 }; GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA)); } } @@ -3954,7 +4078,6 @@ void GuiDisableTooltip(void) { guiTooltip = false; } // Set tooltip string void GuiSetTooltip(const char *tooltip) { guiTooltipPtr = tooltip; } - //---------------------------------------------------------------------------------- // Styles loading functions //---------------------------------------------------------------------------------- @@ -3967,6 +4090,7 @@ void GuiLoadStyle(const char *fileName) #define MAX_LINE_BUFFER_SIZE 256 bool tryBinary = false; + if (!guiStyleLoaded) GuiLoadStyleDefault(); // Try reading the files as text file first FILE *rgsFile = fopen(fileName, "rt"); @@ -4600,7 +4724,7 @@ static int GetTextWidth(const char *text) } } - if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE - ICON_TEXT_PADDING); + if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING); } return (int)textSize.x; @@ -4773,6 +4897,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C // Get text position depending on alignment and iconId //--------------------------------------------------------------------------------- Vector2 textBoundsPosition = { textBounds.x, textBounds.y }; + 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? @@ -4798,6 +4923,8 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C default: break; } + if (textSizeX > textBounds.width && (lines[i] != NULL) && (lines[i][0] != '\0')) textBoundsPosition.x = textBounds.x; + switch (alignmentVertical) { // Only valid in case of wordWrap = 0; @@ -4820,7 +4947,8 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C { // NOTE: We consider icon height, probably different than text size GuiDrawIcon(iconId, (int)textBoundsPosition.x, (int)(textBounds.y + textBounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height)), guiIconScale, tint); - textBoundsPosition.x += (RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); + textBoundsPosition.x += (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); + textBoundsWidthOffset = (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); } #endif // Get size in bytes of text, @@ -4829,9 +4957,15 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n') && (lines[i][c] != '\r'); c++, lineSize++){ } float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; + int lastSpaceIndex = 0; + bool tempWrapCharMode = false; + int textOffsetY = 0; float textOffsetX = 0.0f; float glyphWidth = 0; + + int ellipsisWidth = GetTextWidth("..."); + bool textOverflow = false; for (int c = 0, codepointSize = 0; c < lineSize; c += codepointSize) { int codepoint = GetCodepointNext(&lines[i][c], &codepointSize); @@ -4839,36 +4973,51 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) // but we need to draw all of the bad bytes using the '?' symbol moving one byte - if (codepoint == 0x3f) codepointSize = 1; // TODO: Review not recognized codepoints size + if (codepoint == 0x3f) codepointSize = 1; // TODO: Review not recognized codepoints size + + // Get glyph width to check if it goes out of bounds + if (guiFont.glyphs[index].advanceX == 0) glyphWidth = ((float)guiFont.recs[index].width*scaleFactor); + else glyphWidth = (float)guiFont.glyphs[index].advanceX*scaleFactor; - // Wrap mode text measuring to space to validate if it can be drawn or - // a new line is required + // Wrap mode text measuring, to validate if + // it can be drawn or a new line is required if (wrapMode == TEXT_WRAP_CHAR) { - // Get glyph width to check if it goes out of bounds - if (guiFont.glyphs[index].advanceX == 0) glyphWidth = ((float)guiFont.recs[index].width*scaleFactor); - else glyphWidth = (float)guiFont.glyphs[index].advanceX*scaleFactor; - // Jump to next line if current character reach end of the box limits - if ((textOffsetX + glyphWidth) > textBounds.width) + if ((textOffsetX + glyphWidth) > textBounds.width - textBoundsWidthOffset) { textOffsetX = 0.0f; textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + + if (tempWrapCharMode) // Wrap at char level when too long words + { + wrapMode = TEXT_WRAP_WORD; + tempWrapCharMode = false; + } } } else if (wrapMode == TEXT_WRAP_WORD) { + if (codepoint == 32) lastSpaceIndex = c; + // Get width to next space in line int nextSpaceIndex = 0; float nextSpaceWidth = GetNextSpaceWidth(lines[i] + c, &nextSpaceIndex); - if ((textOffsetX + nextSpaceWidth) > textBounds.width) + int nextSpaceIndex2 = 0; + float nextWordSize = GetNextSpaceWidth(lines[i] + lastSpaceIndex + 1, &nextSpaceIndex2); + + if (nextWordSize > textBounds.width - textBoundsWidthOffset) + { + // Considering the case the next word is longer than bounds + tempWrapCharMode = true; + wrapMode = TEXT_WRAP_CHAR; + } + else if ((textOffsetX + nextSpaceWidth) > textBounds.width - textBoundsWidthOffset) { textOffsetX = 0.0f; textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); } - - // TODO: Consider case: (nextSpaceWidth >= textBounds.width) } if (codepoint == '\n') break; // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint @@ -4881,7 +5030,23 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C if (wrapMode == TEXT_WRAP_NONE) { // Draw only required text glyphs fitting the textBounds.width - if (textOffsetX <= (textBounds.width - glyphWidth)) + if (textSizeX > textBounds.width) + { + if (textOffsetX <= (textBounds.width - glyphWidth - textBoundsWidthOffset - ellipsisWidth)) + { + DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + else if (!textOverflow) + { + textOverflow = true; + + for (int j = 0; j < ellipsisWidth; j += ellipsisWidth/3) + { + DrawTextCodepoint(guiFont, '.', RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX + j, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + } + } + else { DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); } @@ -4937,7 +5102,7 @@ static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, // Draw tooltip using control bounds static void GuiTooltip(Rectangle controlRec) { - if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL) && !guiSliderDragging) + if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL) && !guiControlExclusiveMode) { Vector2 textSize = MeasureTextEx(GuiGetFont(), guiTooltipPtr, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); @@ -5003,7 +5168,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; } } @@ -5163,8 +5328,11 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) if (value > maxValue) value = maxValue; if (value < minValue) value = minValue; - const int valueRange = maxValue - minValue; + int valueRange = maxValue - minValue; + if (valueRange <= 0) valueRange = 1; + int sliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); + if (sliderSize < 1) sliderSize = 1; // TODO: Consider a minimum slider size // Calculate rectangles for all of the components arrowUpLeft = RAYGUI_CLITERAL(Rectangle){ @@ -5205,13 +5373,13 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) { Vector2 mousePoint = GetMousePosition(); - if (guiSliderDragging) // Keep dragging outside of bounds + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds { if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) && !CheckCollisionPointRec(mousePoint, arrowUpLeft) && !CheckCollisionPointRec(mousePoint, arrowDownRight)) { - if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) { state = STATE_PRESSED; @@ -5221,8 +5389,8 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) } else { - guiSliderDragging = false; - guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; } } else if (CheckCollisionPointRec(mousePoint, bounds)) @@ -5236,8 +5404,8 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) // Handle mouse button down if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { - guiSliderDragging = true; - guiSliderActive = bounds; // Store bounds as an identifier when dragging starts + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts // Check arrows click if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); @@ -5437,6 +5605,37 @@ static int TextToInteger(const char *text) return value*sign; } +// Get float value from text +// NOTE: This function replaces atof() [stdlib.h] +// WARNING: Only '.' character is understood as decimal point +static float TextToFloat(const char *text) +{ + float value = 0.0f; + float sign = 1.0f; + + if ((text[0] == '+') || (text[0] == '-')) + { + 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++] != '.') value *= sign; + else + { + float divisor = 10.0f; + for (; ((text[i] >= '0') && (text[i] <= '9')); i++) + { + value += ((float)(text[i] - '0'))/divisor; + divisor = divisor*10.0f; + } + } + + return value; +} + // Encode codepoint into UTF-8 text (char array size returned as parameter) static const char *CodepointToUTF8(int codepoint, int *byteSize) { @@ -5490,21 +5689,21 @@ static int GetCodepointNext(const char *text, int *codepointSize) if (0xf0 == (0xf8 & ptr[0])) { // 4 byte UTF-8 codepoint - if(((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80) || ((ptr[3] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks + if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80) || ((ptr[3] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]); *codepointSize = 4; } else if (0xe0 == (0xf0 & ptr[0])) { // 3 byte UTF-8 codepoint */ - if(((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks + 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; } else if (0xc0 == (0xe0 & ptr[0])) { // 2 byte UTF-8 codepoint - if((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } //10xxxxxx checks + if ((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } //10xxxxxx checks codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]); *codepointSize = 2; } @@ -5515,9 +5714,8 @@ static int GetCodepointNext(const char *text, int *codepointSize) *codepointSize = 1; } - return codepoint; } #endif // RAYGUI_STANDALONE -#endif // RAYGUI_IMPLEMENTATION +#endif // RAYGUI_IMPLEMENTATION \ No newline at end of file From 070c1c9d6357e07672db6e2fc36e37a2a30ee146 Mon Sep 17 00:00:00 2001 From: Anthony Carbajal <5776225+CrackedPixel@users.noreply.github.com> Date: Fri, 9 Aug 2024 02:03:27 -0500 Subject: [PATCH 19/47] update examples missing unloadtexture (#4234) --- examples/models/models_yaw_pitch_roll.c | 1 + examples/shaders/shaders_lightmap.c | 2 ++ examples/shaders/shaders_texture_drawing.c | 1 + examples/shaders/shaders_vertex_displacement.c | 1 + 4 files changed, 5 insertions(+) diff --git a/examples/models/models_yaw_pitch_roll.c b/examples/models/models_yaw_pitch_roll.c index 91a11f73c4ac..5374c035ea95 100644 --- a/examples/models/models_yaw_pitch_roll.c +++ b/examples/models/models_yaw_pitch_roll.c @@ -114,6 +114,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- UnloadModel(model); // Unload model data + UnloadTexture(texture); // Unload texture data CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_lightmap.c b/examples/shaders/shaders_lightmap.c index 01b80a6fbb75..91ed8bfd27ad 100644 --- a/examples/shaders/shaders_lightmap.c +++ b/examples/shaders/shaders_lightmap.c @@ -164,6 +164,8 @@ int main(void) //-------------------------------------------------------------------------------------- UnloadMesh(mesh); // Unload the mesh UnloadShader(shader); // Unload shader + UnloadTexture(texture); // Unload texture + UnloadTexture(light); // Unload texture CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_texture_drawing.c b/examples/shaders/shaders_texture_drawing.c index 52b8967f60f8..ada44299d11e 100644 --- a/examples/shaders/shaders_texture_drawing.c +++ b/examples/shaders/shaders_texture_drawing.c @@ -77,6 +77,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- UnloadShader(shader); + UnloadTexture(texture); CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/shaders/shaders_vertex_displacement.c b/examples/shaders/shaders_vertex_displacement.c index c211d349fa52..207a237d0548 100644 --- a/examples/shaders/shaders_vertex_displacement.c +++ b/examples/shaders/shaders_vertex_displacement.c @@ -110,6 +110,7 @@ int main(void) //-------------------------------------------------------------------------------------- UnloadShader(shader); UnloadModel(planeModel); + UnloadTexture(perlinNoiseMap); CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- From fe9e371f277f8f906f2b4ed9485437fa68f53058 Mon Sep 17 00:00:00 2001 From: NishiOwO <89888985+NishiOwO@users.noreply.github.com> Date: Fri, 9 Aug 2024 16:04:18 +0900 Subject: [PATCH 20/47] [miniaudio] Fixing miniaudio and Makefile for NetBSD (#4212) * fixing miniaudio * another fix for NetBSD * adding wl-r. should not affect other bsd --- src/Makefile | 4 ++-- src/external/miniaudio.h | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index c68a037978f9..4740f1f01b8b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -467,7 +467,7 @@ INCLUDE_PATHS = -I. ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) INCLUDE_PATHS += -Iexternal/glfw/include ifeq ($(PLATFORM_OS),BSD) - INCLUDE_PATHS += -I/usr/local/include + INCLUDE_PATHS += -I/usr/local/include -I/usr/pkg/include -I/usr/X11R7/include endif endif ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) @@ -522,7 +522,7 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) endif ifeq ($(PLATFORM_OS),BSD) - LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).$(RAYLIB_API_VERSION).so -Lsrc -L/usr/local/lib + LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).$(RAYLIB_API_VERSION).so -Lsrc -L/usr/local/lib -L/usr/pkg/lib -Wl,-R/usr/pkg/lib endif endif ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index 787c626ad18c..ad113337ee95 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -36078,9 +36078,15 @@ static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext ma_uint32 channels; ma_uint32 sampleRate; +#ifdef __NetBSD__ + if (ioctl(fd, AUDIO_GETFORMAT, &fdInfo) < 0) { + return MA_ERROR; + } +#else if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { return MA_ERROR; } +#endif if (deviceType == ma_device_type_playback) { channels = fdInfo.play.channels; @@ -36358,7 +36364,11 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c /* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */ int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0); if (fdctl != -1) { +#ifdef __NetBSD__ + fdInfoResult = ioctl(fdctl, AUDIO_GETFORMAT, &fdInfo); +#else fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo); +#endif close(fdctl); } } From 2590a30d04b4aa4d35787052af55fb96bd699c42 Mon Sep 17 00:00:00 2001 From: Maxim Knyazkin <42771130+MaximKn1@users.noreply.github.com> Date: Fri, 9 Aug 2024 10:05:46 +0300 Subject: [PATCH 21/47] [rlgl] Adding warnings in case OpenGL 4.3 is not enabled (#4202) * Adding warnings for OpenGL 4.3 * Removed logging from frequently called functions --- src/rlgl.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/rlgl.h b/src/rlgl.h index 8ee6026446bb..baf89a78231c 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -4083,6 +4083,8 @@ unsigned int rlCompileShader(const char *shaderCode, int type) //case GL_GEOMETRY_SHADER: #if defined(GRAPHICS_API_OPENGL_43) case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile compute shader code", shader); break; + #else + case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43", shader); break; #endif default: break; } @@ -4108,6 +4110,8 @@ unsigned int rlCompileShader(const char *shaderCode, int type) //case GL_GEOMETRY_SHADER: #if defined(GRAPHICS_API_OPENGL_43) case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader compiled successfully", shader); break; + #else + case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43", shader); break; #endif default: break; } @@ -4348,6 +4352,8 @@ unsigned int rlLoadComputeShaderProgram(unsigned int shaderId) TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader program loaded successfully", program); } +#else + TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43"); #endif return program; @@ -4372,6 +4378,8 @@ unsigned int rlLoadShaderBuffer(unsigned int size, const void *data, int usageHi glBufferData(GL_SHADER_STORAGE_BUFFER, size, data, usageHint? usageHint : RL_STREAM_COPY); if (data == NULL) glClearBufferData(GL_SHADER_STORAGE_BUFFER, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, NULL); // Clear buffer data to 0 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); +#else + TRACELOG(RL_LOG_WARNING, "SSBO: SSBO not enabled. Define GRAPHICS_API_OPENGL_43"); #endif return ssbo; @@ -4382,7 +4390,10 @@ void rlUnloadShaderBuffer(unsigned int ssboId) { #if defined(GRAPHICS_API_OPENGL_43) glDeleteBuffers(1, &ssboId); +#else + TRACELOG(RL_LOG_WARNING, "SSBO: SSBO not enabled. Define GRAPHICS_API_OPENGL_43"); #endif + } // Update SSBO buffer data @@ -4442,6 +4453,8 @@ void rlBindImageTexture(unsigned int id, unsigned int index, int format, bool re rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); glBindImageTexture(index, id, 0, 0, 0, readonly? GL_READ_ONLY : GL_READ_WRITE, glInternalFormat); +#else + TRACELOG(RL_LOG_WARNING, "TEXTURE: Image texture binding not enabled. Define GRAPHICS_API_OPENGL_43"); #endif } From 243801c2d1592910669ff11bc6e9b45c2e7507d9 Mon Sep 17 00:00:00 2001 From: Anthony Carbajal <5776225+CrackedPixel@users.noreply.github.com> Date: Fri, 9 Aug 2024 02:07:56 -0500 Subject: [PATCH 22/47] update text writing anim (#4230) --- examples/text/text_writing_anim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/text/text_writing_anim.c b/examples/text/text_writing_anim.c index 1e7cd569f48d..8e2820fc93b5 100644 --- a/examples/text/text_writing_anim.c +++ b/examples/text/text_writing_anim.c @@ -52,7 +52,7 @@ int main(void) DrawText(TextSubtext(message, 0, framesCounter/10), 210, 160, 20, MAROON); DrawText("PRESS [ENTER] to RESTART!", 240, 260, 20, LIGHTGRAY); - DrawText("PRESS [SPACE] to SPEED UP!", 239, 300, 20, LIGHTGRAY); + DrawText("HOLD [SPACE] to SPEED UP!", 239, 300, 20, LIGHTGRAY); EndDrawing(); //---------------------------------------------------------------------------------- From 85c6489d811fc7f5de4d379ad10b4bb071bc871c Mon Sep 17 00:00:00 2001 From: Anthony Carbajal <5776225+CrackedPixel@users.noreply.github.com> Date: Fri, 9 Aug 2024 02:12:26 -0500 Subject: [PATCH 23/47] update models box collisions (#4224) --- examples/models/models_box_collisions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/models/models_box_collisions.c b/examples/models/models_box_collisions.c index 936f65f38a3e..4d98a776a539 100644 --- a/examples/models/models_box_collisions.c +++ b/examples/models/models_box_collisions.c @@ -109,7 +109,7 @@ int main(void) EndMode3D(); - DrawText("Move player with cursors to collide", 220, 40, 20, GRAY); + DrawText("Move player with arrow keys to collide", 220, 40, 20, GRAY); DrawFPS(10, 10); From 06aeb214296a9c51660e4695114244d4acfc58b1 Mon Sep 17 00:00:00 2001 From: Anthony Carbajal <5776225+CrackedPixel@users.noreply.github.com> Date: Fri, 9 Aug 2024 02:15:07 -0500 Subject: [PATCH 24/47] update shaders basic pbr (#4225) --- examples/shaders/shaders_basic_pbr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shaders/shaders_basic_pbr.c b/examples/shaders/shaders_basic_pbr.c index d025ab8a9d88..c3f2bad88bc3 100644 --- a/examples/shaders/shaders_basic_pbr.c +++ b/examples/shaders/shaders_basic_pbr.c @@ -236,7 +236,7 @@ int main() float emissiveIntensity = 0.01f; SetShaderValue(shader, emissiveIntensityLoc, &emissiveIntensity, SHADER_UNIFORM_FLOAT); - DrawModel(car, (Vector3){ 0.0f, 0.0f, 0.0f }, 0.005f, WHITE); // Draw car model + DrawModel(car, (Vector3){ 0.0f, 0.0f, 0.0f }, 0.25f, WHITE); // Draw car model // Draw spheres to show the lights positions for (int i = 0; i < MAX_LIGHTS; i++) From 3c5bdae7abffeefe90d927572b5ea443ea50411b Mon Sep 17 00:00:00 2001 From: Anthony Carbajal <5776225+CrackedPixel@users.noreply.github.com> Date: Fri, 9 Aug 2024 02:16:05 -0500 Subject: [PATCH 25/47] update shapes bouncing ball (#4226) --- examples/shapes/shapes_bouncing_ball.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shapes/shapes_bouncing_ball.c b/examples/shapes/shapes_bouncing_ball.c index 7a30681639e2..df0c37c1c95f 100644 --- a/examples/shapes/shapes_bouncing_ball.c +++ b/examples/shapes/shapes_bouncing_ball.c @@ -62,7 +62,7 @@ int main(void) ClearBackground(RAYWHITE); DrawCircleV(ballPosition, (float)ballRadius, MAROON); - //DrawText("PRESS SPACE to PAUSE BALL MOVEMENT", 10, GetScreenHeight() - 25, 20, LIGHTGRAY); + DrawText("PRESS SPACE to PAUSE BALL MOVEMENT", 10, GetScreenHeight() - 25, 20, LIGHTGRAY); // On pause, we draw a blinking message if (pause && ((framesCounter/30)%2)) DrawText("PAUSED", 350, 200, 30, GRAY); From e4529ff8f9d2e03f29e5271fa3eea0c603174925 Mon Sep 17 00:00:00 2001 From: Anthony Carbajal <5776225+CrackedPixel@users.noreply.github.com> Date: Fri, 9 Aug 2024 02:18:00 -0500 Subject: [PATCH 26/47] update text input box (#4229) --- examples/text/text_input_box.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/text/text_input_box.c b/examples/text/text_input_box.c index eeb338f1f7b3..c530577111a7 100644 --- a/examples/text/text_input_box.c +++ b/examples/text/text_input_box.c @@ -35,7 +35,7 @@ int main(void) int framesCounter = 0; - SetTargetFPS(10); // Set our game to run at 10 frames-per-second + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop From 13491a485a5f29d00f8fe23e796501bec67a7378 Mon Sep 17 00:00:00 2001 From: Maxim Knyazkin <42771130+MaximKn1@users.noreply.github.com> Date: Fri, 9 Aug 2024 20:02:18 +0300 Subject: [PATCH 27/47] Fixed compilation for OpenGL ES (#4243) --- src/rlgl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index baf89a78231c..19c529398be2 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -4083,7 +4083,7 @@ unsigned int rlCompileShader(const char *shaderCode, int type) //case GL_GEOMETRY_SHADER: #if defined(GRAPHICS_API_OPENGL_43) case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile compute shader code", shader); break; - #else + #elif defined(GRAPHICS_API_OPENGL_33) case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43", shader); break; #endif default: break; @@ -4110,7 +4110,7 @@ unsigned int rlCompileShader(const char *shaderCode, int type) //case GL_GEOMETRY_SHADER: #if defined(GRAPHICS_API_OPENGL_43) case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader compiled successfully", shader); break; - #else + #elif defined(GRAPHICS_API_OPENGL_33) case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43", shader); break; #endif default: break; From 46cb6af4375da00563c68c6bbe4c24dcdf885b60 Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Fri, 9 Aug 2024 14:03:14 -0300 Subject: [PATCH 28/47] Fix core_input_gamepad_info example so all buttons are displayed within the window (#4241) --- examples/core/core_input_gamepad_info.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/core/core_input_gamepad_info.c b/examples/core/core_input_gamepad_info.c index 66fedda90065..0962946810ee 100644 --- a/examples/core/core_input_gamepad_info.c +++ b/examples/core/core_input_gamepad_info.c @@ -47,25 +47,25 @@ int main(void) ClearBackground(RAYWHITE); - for (int i = 0, y = 10; i < 4; i++) // MAX_GAMEPADS = 4 + for (int i = 0, y = 5; i < 4; i++) // MAX_GAMEPADS = 4 { if (IsGamepadAvailable(i)) { - DrawText(TextFormat("Gamepad name: %s", GetGamepadName(i)), 10, y, 20, BLACK); - y += 30; - DrawText(TextFormat("\tAxis count: %d", GetGamepadAxisCount(i)), 10, y, 20, BLACK); - y += 30; + DrawText(TextFormat("Gamepad name: %s", GetGamepadName(i)), 10, y, 10, BLACK); + y += 11; + DrawText(TextFormat("\tAxis count: %d", GetGamepadAxisCount(i)), 10, y, 10, BLACK); + y += 11; for (int axis = 0; axis < GetGamepadAxisCount(i); axis++) { - DrawText(TextFormat("\tAxis %d = %f", axis, GetGamepadAxisMovement(i, axis)), 10, y, 20, BLACK); - y += 30; + DrawText(TextFormat("\tAxis %d = %f", axis, GetGamepadAxisMovement(i, axis)), 10, y, 10, BLACK); + y += 11; } for (int button = 0; button < 32; button++) { - DrawText(TextFormat("\tButton %d = %d", button, IsGamepadButtonDown(i, button)), 10, y, 20, BLACK); - y += 30; + DrawText(TextFormat("\tButton %d = %d", button, IsGamepadButtonDown(i, button)), 10, y, 10, BLACK); + y += 11; } } } @@ -80,4 +80,4 @@ int main(void) //-------------------------------------------------------------------------------------- CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- -} \ No newline at end of file +} From 418b8780533e6d7822aefcd1a1dd3cb6cca95fa5 Mon Sep 17 00:00:00 2001 From: Anthony Carbajal <5776225+CrackedPixel@users.noreply.github.com> Date: Sat, 10 Aug 2024 13:07:23 -0500 Subject: [PATCH 29/47] [Examples] set FPS to 60 (#4235) * set FPS to 60 * remove extra commented lines --- examples/core/core_vr_simulator.c | 2 +- examples/textures/textures_blend_modes.c | 3 +++ examples/textures/textures_image_rotate.c | 2 ++ examples/textures/textures_logo_raylib.c | 2 ++ examples/textures/textures_raw_data.c | 2 ++ examples/textures/textures_sprite_explosion.c | 4 ++-- examples/textures/textures_to_image.c | 2 ++ 7 files changed, 14 insertions(+), 3 deletions(-) diff --git a/examples/core/core_vr_simulator.c b/examples/core/core_vr_simulator.c index c98fce9313a9..021698edb35d 100644 --- a/examples/core/core_vr_simulator.c +++ b/examples/core/core_vr_simulator.c @@ -100,7 +100,7 @@ int main(void) DisableCursor(); // Limit cursor to relative movement inside the window - SetTargetFPS(90); // Set our game to run at 90 frames-per-second + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop diff --git a/examples/textures/textures_blend_modes.c b/examples/textures/textures_blend_modes.c index 660aa57a789b..d97432cb222f 100644 --- a/examples/textures/textures_blend_modes.c +++ b/examples/textures/textures_blend_modes.c @@ -43,6 +43,9 @@ int main(void) const int blendCountMax = 4; BlendMode blendMode = 0; + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //--------------------------------------------------------------------------------------- + // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { diff --git a/examples/textures/textures_image_rotate.c b/examples/textures/textures_image_rotate.c index 51724e0372ed..94c42034b6eb 100644 --- a/examples/textures/textures_image_rotate.c +++ b/examples/textures/textures_image_rotate.c @@ -43,6 +43,8 @@ int main(void) textures[2] = LoadTextureFromImage(imageNeg90); int currentTexture = 0; + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //--------------------------------------------------------------------------------------- // Main game loop diff --git a/examples/textures/textures_logo_raylib.c b/examples/textures/textures_logo_raylib.c index f170dab48b2f..35baa1223747 100644 --- a/examples/textures/textures_logo_raylib.c +++ b/examples/textures/textures_logo_raylib.c @@ -27,6 +27,8 @@ int main(void) // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) Texture2D texture = LoadTexture("resources/raylib_logo.png"); // Texture loading + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //--------------------------------------------------------------------------------------- // Main game loop diff --git a/examples/textures/textures_raw_data.c b/examples/textures/textures_raw_data.c index 38229f1ba49e..6fb41b143ab7 100644 --- a/examples/textures/textures_raw_data.c +++ b/examples/textures/textures_raw_data.c @@ -63,6 +63,8 @@ int main(void) Texture2D checked = LoadTextureFromImage(checkedIm); UnloadImage(checkedIm); // Unload CPU (RAM) image data (pixels) + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //--------------------------------------------------------------------------------------- // Main game loop diff --git a/examples/textures/textures_sprite_explosion.c b/examples/textures/textures_sprite_explosion.c index 06b4f1dad769..a65426cb6267 100644 --- a/examples/textures/textures_sprite_explosion.c +++ b/examples/textures/textures_sprite_explosion.c @@ -48,8 +48,8 @@ int main(void) bool active = false; int framesCounter = 0; - SetTargetFPS(120); - //-------------------------------------------------------------------------------------- + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //--------------------------------------------------------------------------------------- // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key diff --git a/examples/textures/textures_to_image.c b/examples/textures/textures_to_image.c index 2d09623ebb28..e1d88c4cd498 100644 --- a/examples/textures/textures_to_image.c +++ b/examples/textures/textures_to_image.c @@ -38,6 +38,8 @@ int main(void) texture = LoadTextureFromImage(image); // Recreate texture from retrieved image data (RAM -> VRAM) UnloadImage(image); // Unload retrieved image data from CPU memory (RAM) + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //--------------------------------------------------------------------------------------- // Main game loop From 65c400354646fd43f54b9f832acc80ee6f63d1e2 Mon Sep 17 00:00:00 2001 From: hanaxars <156359933+hanaxars@users.noreply.github.com> Date: Mon, 12 Aug 2024 19:47:52 +0300 Subject: [PATCH 30/47] Make camera movement independant of framerate (#4247) Instead of moving camera with constant speed per frame, speed is multiplied with delta time before movement. --- src/rcamera.h | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index d3042d3064bc..c7f6fed3ac43 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -196,12 +196,12 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define CAMERA_MOVE_SPEED 0.09f +#define CAMERA_MOVE_SPEED 5.4f // Units per second #define CAMERA_ROTATION_SPEED 0.03f #define CAMERA_PAN_SPEED 0.2f // Camera mouse movement sensitivity -#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f // TODO: it should be independant of framerate +#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f // Camera orbital speed in CAMERA_ORBITAL mode #define CAMERA_ORBITAL_SPEED 0.5f // Radians per second @@ -481,10 +481,11 @@ void UpdateCamera(Camera *camera, int mode) } // Keyboard support - if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + float cameraMoveSpeed = CAMERA_MOVE_SPEED*GetFrameTime(); + if (IsKeyDown(KEY_W)) CameraMoveForward(camera, cameraMoveSpeed, moveInWorldPlane); + if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -cameraMoveSpeed, moveInWorldPlane); + if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -cameraMoveSpeed, moveInWorldPlane); + if (IsKeyDown(KEY_D)) CameraMoveRight(camera, cameraMoveSpeed, moveInWorldPlane); // Gamepad movement if (IsGamepadAvailable(0)) @@ -493,16 +494,16 @@ void UpdateCamera(Camera *camera, int mode) CameraYaw(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X)*2)*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); CameraPitch(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y)*2)*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); - if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) <= -0.25f) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); - if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) <= -0.25f) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) >= 0.25f) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) >= 0.25f) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) <= -0.25f) CameraMoveForward(camera, cameraMoveSpeed, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) <= -0.25f) CameraMoveRight(camera, -cameraMoveSpeed, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) >= 0.25f) CameraMoveForward(camera, -cameraMoveSpeed, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) >= 0.25f) CameraMoveRight(camera, cameraMoveSpeed, moveInWorldPlane); } if (mode == CAMERA_FREE) { - if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); - if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); + if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, cameraMoveSpeed); + if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -cameraMoveSpeed); } } From 6e644a27fc1c3d3499750a0e58a0ba5c2a4a0ed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1zaro=20Albuquerque?= <33807434+lzralbu@users.noreply.github.com> Date: Tue, 13 Aug 2024 13:16:07 -0400 Subject: [PATCH 31/47] Change some global variables to have internal linkage (#4252) * Change some global variables to have internal linkage * Update rcore.c * Update rcore.c --- src/rcore.c | 6 +++--- src/rshapes.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 93ae4f3bb598..5485ce8ce032 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -367,9 +367,9 @@ static int screenshotCounter = 0; // Screenshots counter #endif #if defined(SUPPORT_GIF_RECORDING) -unsigned int gifFrameCounter = 0; // GIF frames counter -bool gifRecording = false; // GIF recording state -MsfGifState gifState = { 0 }; // MSGIF context state +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) diff --git a/src/rshapes.c b/src/rshapes.c index 89339708a319..676b2521a06b 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -79,8 +79,8 @@ //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -Texture2D texShapes = { 1, 1, 1, 1, 7 }; // Texture used on shapes drawing (white pixel loaded by rlgl) -Rectangle texShapesRec = { 0.0f, 0.0f, 1.0f, 1.0f }; // Texture source rectangle used on shapes drawing +static Texture2D texShapes = { 1, 1, 1, 1, 7 }; // Texture used on shapes drawing (white pixel loaded by rlgl) +static Rectangle texShapesRec = { 0.0f, 0.0f, 1.0f, 1.0f }; // Texture source rectangle used on shapes drawing //---------------------------------------------------------------------------------- // Module specific Functions Declaration From 63ae57d2e31afa8bc8cab7f17f0e44afa4e7552d Mon Sep 17 00:00:00 2001 From: Brian E <72316548+Brian-ED@users.noreply.github.com> Date: Thu, 15 Aug 2024 09:05:34 +0100 Subject: [PATCH 32/47] Added raylib-APL to BINDINGS.md (#4253) * Added raylib-APL to BINDINGS.md * added back the newline * Made APL binding be version 5.0 instead of auto Also alligned the bars of the new entry. --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 3e840d58791f..7fd0dfd93a5e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -83,6 +83,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers | [raylib-raku](https://github.com/vushu/raylib-raku) | **auto** | [Raku](https://www.raku.org) | Artistic License 2.0 | | [Raylib.lean](https://github.com/KislyjKisel/Raylib.lean) | 4.5 | [Lean4](https://lean-lang.org) | BSD-3-Clause | | [raylib-cobol](https://codeberg.org/glowiak/raylib-cobol) | **auto** | [COBOL](https://gnucobol.sourceforge.io) | Public domain | +| [raylib-apl](https://github.com/Brian-ED/raylib-apl) | **5.0** | [Dyalog APL](https://www.dyalog.com/) | MIT | ### Utility Wrapers From fa374f9cc902a37836cc66ea7d958d9be87057a3 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 17 Aug 2024 00:44:33 +0200 Subject: [PATCH 33/47] Update webassembly.yml --- .github/workflows/webassembly.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/webassembly.yml b/.github/workflows/webassembly.yml index f9e51680dd90..f676877fdb41 100644 --- a/.github/workflows/webassembly.yml +++ b/.github/workflows/webassembly.yml @@ -29,7 +29,7 @@ jobs: - name: Setup emsdk uses: mymindstorm/setup-emsdk@v14 with: - version: 3.1.54 + version: 3.1.64 actions-cache-folder: 'emsdk-cache' - name: Setup Release Version From f70d8a33cb8a3cefe0cc261e945d4f6715c6c0bc Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 17 Aug 2024 00:46:08 +0200 Subject: [PATCH 34/47] REVIEWED: Shader load failing returns 0, instead of fallback --- src/rlgl.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 19c529398be2..5fb9497cd20a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3994,18 +3994,18 @@ unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode) unsigned int fragmentShaderId = 0; // Compile vertex shader (if provided) + // NOTE: If not vertex shader is provided, use default one if (vsCode != NULL) vertexShaderId = rlCompileShader(vsCode, GL_VERTEX_SHADER); - // In case no vertex shader was provided or compilation failed, we use default vertex shader - if (vertexShaderId == 0) vertexShaderId = RLGL.State.defaultVShaderId; + else vertexShaderId = RLGL.State.defaultVShaderId; // Compile fragment shader (if provided) + // NOTE: If not vertex shader is provided, use default one if (fsCode != NULL) fragmentShaderId = rlCompileShader(fsCode, GL_FRAGMENT_SHADER); - // In case no fragment shader was provided or compilation failed, we use default fragment shader - if (fragmentShaderId == 0) fragmentShaderId = RLGL.State.defaultFShaderId; + else fragmentShaderId = RLGL.State.defaultFShaderId; // In case vertex and fragment shader are the default ones, no need to recompile, we can just assign the default shader program id if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShaderId; - else + else if ((vertexShaderId > 0) && (fragmentShaderId > 0)) { // One of or both shader are new, we need to compile a new shader program id = rlLoadShaderProgram(vertexShaderId, fragmentShaderId); @@ -4100,6 +4100,8 @@ unsigned int rlCompileShader(const char *shaderCode, int type) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Compile error: %s", shader, log); RL_FREE(log); } + + shader = 0; } else { From 308b77cd42c42406a3bd4eac250761476ff0407e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1zaro=20Albuquerque?= <33807434+lzralbu@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:07:23 -0400 Subject: [PATCH 35/47] Fix warnings (#4251) * Update rmodels.c fix these warnings: ``` /src/rmodels.c:5744:17: warning: missing initializer for field 'w' of 'Vector4' [-Wmissing-field-initializers] [build] 5744 | Vector4 outTangent1 = {tmp[0], tmp[1], tmp[2]}; [build] | ^~~~~~~ ``` * Update rcore_web.c fix warnings --- src/platforms/rcore_web.c | 22 +++++++++++----------- src/rmodels.c | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 937e15acd491..ff7a92dbccce 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -129,7 +129,7 @@ static void CursorEnterCallback(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); // Emscripten input callback events @@ -981,7 +981,7 @@ void PollInputEvents(void) default: break; } - if (button != -1) // Check for valid button + if (button + 1 != 0) // Check for valid button { if (gamepadState.digitalButton[j] == 1) { @@ -1572,12 +1572,12 @@ static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const Emscripte } // Register window resize event -static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData) -{ - // TODO: Implement EmscriptenWindowResizedCallback()? +// static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData) +// { +// // TODO: Implement EmscriptenWindowResizedCallback()? - return 1; // The event was consumed by the callback handler -} +// return 1; // The event was consumed by the callback handler +// } EM_JS(int, GetWindowInnerWidth, (), { return window.innerWidth; }); EM_JS(int, GetWindowInnerHeight, (), { return window.innerHeight; }); @@ -1593,11 +1593,11 @@ static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent * int width = GetWindowInnerWidth(); int height = GetWindowInnerHeight(); - if (width < CORE.Window.screenMin.width) width = CORE.Window.screenMin.width; - else if (width > CORE.Window.screenMax.width && CORE.Window.screenMax.width > 0) width = CORE.Window.screenMax.width; + 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 < CORE.Window.screenMin.height) height = CORE.Window.screenMin.height; - else if (height > CORE.Window.screenMax.height && CORE.Window.screenMax.height > 0) height = CORE.Window.screenMax.height; + 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("#canvas", width, height); diff --git a/src/rmodels.c b/src/rmodels.c index c1d56765e80d..36a7e254d1f3 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5741,11 +5741,11 @@ static bool GetPoseAtTimeGLTF(cgltf_interpolation_type interpolationType, cgltf_ cgltf_accessor_read_float(output, 3*keyframe+1, tmp, 4); Vector4 v1 = {tmp[0], tmp[1], tmp[2], tmp[3]}; cgltf_accessor_read_float(output, 3*keyframe+2, tmp, 4); - Vector4 outTangent1 = {tmp[0], tmp[1], tmp[2]}; + Vector4 outTangent1 = {tmp[0], tmp[1], tmp[2], 0.0f}; cgltf_accessor_read_float(output, 3*(keyframe+1)+1, tmp, 4); Vector4 v2 = {tmp[0], tmp[1], tmp[2], tmp[3]}; cgltf_accessor_read_float(output, 3*(keyframe+1), tmp, 4); - Vector4 inTangent2 = {tmp[0], tmp[1], tmp[2]}; + Vector4 inTangent2 = {tmp[0], tmp[1], tmp[2], 0.0f}; Vector4 *r = data; v1 = QuaternionNormalize(v1); From b432aa2b9cd5eb35391636b6e147db8d9a9cf1cc Mon Sep 17 00:00:00 2001 From: Colleague Riley Date: Sat, 17 Aug 2024 08:00:54 -0400 Subject: [PATCH 36/47] Update RGFW (#4259) * update RGFW * fix bug with GetCurrentMonitor * update RGFW * update RGFW * clean up merge * update RGFW --- src/external/RGFW.h | 3178 ++++++++++++++++++++-------- src/platforms/rcore_desktop_rgfw.c | 6 +- 2 files changed, 2274 insertions(+), 910 deletions(-) diff --git a/src/external/RGFW.h b/src/external/RGFW.h index 5420ccab1eab..367a46fa608e 100644 --- a/src/external/RGFW.h +++ b/src/external/RGFW.h @@ -57,6 +57,8 @@ #define RGFW_EXPORT - Use when building RGFW #define RGFW_IMPORT - Use when linking with RGFW (not as a single-header) + + #define RGFW_STD_INT - force the use stdint.h (for systems that might not have stdint.h (msvc)) */ /* @@ -81,6 +83,7 @@ Code-Nycticebus -> bug fixes Rob Rohan -> X11 bugs and missing features, MacOS/Cocoa fixing memory issues/bugs AICDG (@THISISAGOODNAME) -> vulkan support (example) + @Easymode -> support, testing/debugging, bug fixes and reviews */ #if _MSC_VER @@ -93,6 +96,11 @@ #ifndef RGFW_MALLOC #include + + #ifndef __USE_POSIX199309 + #define __USE_POSIX199309 + #endif + #include #define RGFW_MALLOC malloc #define RGFW_CALLOC calloc @@ -131,7 +139,7 @@ #endif #ifndef RGFWDEF - #ifdef __APPLE__ + #ifdef __clang__ #define RGFWDEF static inline #else #define RGFWDEF inline @@ -146,7 +154,7 @@ #define RGFW_UNUSED(x) (void)(x); #endif -#ifdef __cplusplus +#if defined(__cplusplus) && !defined(__EMSCRIPTEN__) extern "C" { #endif @@ -156,7 +164,7 @@ #define RGFW_HEADER #if !defined(u8) - #if defined(_MSC_VER) || defined(__SYMBIAN32__) /* MSVC might not have stdint.h */ + #if ((defined(_MSC_VER) || defined(__SYMBIAN32__)) && !defined(RGFW_STD_INT)) /* MSVC might not have stdint.h */ typedef unsigned char u8; typedef signed char i8; typedef unsigned short u16; @@ -182,10 +190,11 @@ #if !defined(b8) /* RGFW bool type */ typedef u8 b8; typedef u32 b32; - #define RGFW_TRUE 1 - #define RGFW_FALSE 0 #endif +#define RGFW_TRUE 1 +#define RGFW_FALSE 0 + /* thse OS macros looks better & are standardized */ /* plus it helps with cross-compiling */ @@ -242,6 +251,14 @@ #endif #endif +#elif defined(RGFW_WAYLAND) + #if !defined(RGFW_NO_API) && (!defined(RGFW_BUFFER) || defined(RGFW_OPENGL)) + #define RGFW_EGL + #define RGFW_OPENGL + #include + #endif + + #include #elif (defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11)) && !defined(RGFW_WEBASM) #define RGFW_MACOS_X11 #define RGFW_X11 @@ -299,65 +316,65 @@ #define RGFW_NO_GPU_RENDER (1L<<14) /* don't render (using the GPU based API)*/ #define RGFW_NO_CPU_RENDER (1L<<15) /* don't render (using the CPU based buffer rendering)*/ +#define RGFW_WINDOW_HIDE (1L << 16)/* the window is hidden */ + +typedef RGFW_ENUM(u8, RGFW_event_types) { + /*! event codes */ + RGFW_keyPressed = 1, /* a key has been pressed */ + RGFW_keyReleased, /*!< a key has been released*/ + /*! key event note + the code of the key pressed is stored in + RGFW_Event.keyCode + !!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! + + while a string version is stored in + RGFW_Event.KeyString + + RGFW_Event.lockState holds the current lockState + this means if CapsLock, NumLock are active or not + */ + RGFW_mouseButtonPressed, /*!< a mouse button has been pressed (left,middle,right)*/ + RGFW_mouseButtonReleased, /*!< a mouse button has been released (left,middle,right)*/ + RGFW_mousePosChanged, /*!< the position of the mouse has been changed*/ + /*! mouse event note + the x and y of the mouse can be found in the vector, RGFW_Event.point -/*! event codes */ -#define RGFW_keyPressed 2 /* a key has been pressed */ -#define RGFW_keyReleased 3 /*!< a key has been released*/ -/*! key event note - the code of the key pressed is stored in - RGFW_Event.keyCode - !!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! - - while a string version is stored in - RGFW_Event.KeyString - - RGFW_Event.lockState holds the current lockState - this means if CapsLock, NumLock are active or not -*/ -#define RGFW_mouseButtonPressed 4 /*!< a mouse button has been pressed (left,middle,right)*/ -#define RGFW_mouseButtonReleased 5 /*!< a mouse button has been released (left,middle,right)*/ -#define RGFW_mousePosChanged 6 /*!< the position of the mouse has been changed*/ -/*! mouse event note - the x and y of the mouse can be found in the vector, RGFW_Event.point - - RGFW_Event.button holds which mouse button was pressed -*/ -#define RGFW_jsButtonPressed 7 /*!< a joystick button was pressed */ -#define RGFW_jsButtonReleased 8 /*!< a joystick button was released */ -#define RGFW_jsAxisMove 9 /*!< an axis of a joystick was moved*/ -/*! joystick event note - RGFW_Event.joystick holds which joystick was altered, if any - RGFW_Event.button holds which joystick button was pressed - - RGFW_Event.axis holds the data of all the axis - RGFW_Event.axisCount says how many axis there are -*/ -#define RGFW_windowMoved 10 /*!< the window was moved (by the user) */ -#define RGFW_windowResized 11 /*!< the window was resized (by the user), [on webASM this means the browser was resized] */ - -#define RGFW_focusIn 12 /*!< window is in focus now */ -#define RGFW_focusOut 13 /*!< window is out of focus now */ - -#define RGFW_mouseEnter 14 /* mouse entered the window */ -#define RGFW_mouseLeave 15 /* mouse left the window */ - -#define RGFW_windowRefresh 16 /* The window content needs to be refreshed */ - -/* attribs change event note - The event data is sent straight to the window structure - with win->r.x, win->r.y, win->r.w and win->r.h -*/ -#define RGFW_quit 33 /*!< the user clicked the quit button*/ -#define RGFW_dnd 34 /*!< a file has been dropped into the window*/ -#define RGFW_dnd_init 35 /*!< the start of a dnd event, when the place where the file drop is known */ -/* dnd data note - The x and y coords of the drop are stored in the vector RGFW_Event.point + RGFW_Event.button holds which mouse button was pressed + */ + RGFW_jsButtonPressed, /*!< a joystick button was pressed */ + RGFW_jsButtonReleased, /*!< a joystick button was released */ + RGFW_jsAxisMove, /*!< an axis of a joystick was moved*/ + /*! joystick event note + RGFW_Event.joystick holds which joystick was altered, if any + RGFW_Event.button holds which joystick button was pressed + + RGFW_Event.axis holds the data of all the axis + RGFW_Event.axisCount says how many axis there are + */ + RGFW_windowMoved, /*!< the window was moved (by the user) */ + RGFW_windowResized, /*!< the window was resized (by the user), [on webASM this means the browser was resized] */ + RGFW_focusIn, /*!< window is in focus now */ + RGFW_focusOut, /*!< window is out of focus now */ + RGFW_mouseEnter, /* mouse entered the window */ + RGFW_mouseLeave, /* mouse left the window */ + RGFW_windowRefresh, /* The window content needs to be refreshed */ + + /* attribs change event note + The event data is sent straight to the window structure + with win->r.x, win->r.y, win->r.w and win->r.h + */ + RGFW_quit, /*!< the user clicked the quit button*/ + RGFW_dnd, /*!< a file has been dropped into the window*/ + RGFW_dnd_init /*!< the start of a dnd event, when the place where the file drop is known */ + /* dnd data note + The x and y coords of the drop are stored in the vector RGFW_Event.point - RGFW_Event.droppedFilesCount holds how many files were dropped + RGFW_Event.droppedFilesCount holds how many files were dropped - This is also the size of the array which stores all the dropped file string, - RGFW_Event.droppedFiles -*/ + This is also the size of the array which stores all the dropped file string, + RGFW_Event.droppedFiles + */ +}; /*! mouse button codes (RGFW_Event.button) */ #define RGFW_mouseLeft 1 /*!< left mouse button is pressed*/ @@ -381,64 +398,72 @@ /*! joystick button codes (based on xbox/playstation), you may need to change these values per controller */ #ifndef RGFW_joystick_codes typedef RGFW_ENUM(u8, RGFW_joystick_codes) { - RGFW_JS_A = 0, /* or PS X button */ - RGFW_JS_B = 1, /* or PS circle button */ - RGFW_JS_Y = 2, /* or PS triangle button */ - RGFW_JS_X = 3, /* or PS square button */ - RGFW_JS_START = 9, /* start button */ - RGFW_JS_SELECT = 8, /* select button */ - RGFW_JS_HOME = 10, /* home button */ - RGFW_JS_UP = 13, /* dpad up */ - RGFW_JS_DOWN = 14, /* dpad down*/ - RGFW_JS_LEFT = 15, /* dpad left */ - RGFW_JS_RIGHT = 16, /* dpad right */ - RGFW_JS_L1 = 4, /* left bump */ - RGFW_JS_L2 = 5, /* left trigger*/ - RGFW_JS_R1 = 6, /* right bumper */ - RGFW_JS_R2 = 7, /* right trigger */ + RGFW_JS_A = 0, /*!< or PS X button */ + RGFW_JS_B = 1, /*!< or PS circle button */ + RGFW_JS_Y = 2, /*!< or PS triangle button */ + RGFW_JS_X = 3, /*!< or PS square button */ + RGFW_JS_START = 9, /*!< start button */ + RGFW_JS_SELECT = 8, /*!< select button */ + RGFW_JS_HOME = 10, /*!< home button */ + RGFW_JS_UP = 13, /*!< dpad up */ + RGFW_JS_DOWN = 14, /*!< dpad down*/ + RGFW_JS_LEFT = 15, /*!< dpad left */ + RGFW_JS_RIGHT = 16, /*!< dpad right */ + RGFW_JS_L1 = 4, /*!< left bump */ + RGFW_JS_L2 = 5, /*!< left trigger*/ + RGFW_JS_R1 = 6, /*!< right bumper */ + RGFW_JS_R2 = 7, /*!< right trigger */ }; #endif -/* basic vector type, if there's not already a point/vector type of choice */ +/*! basic vector type, if there's not already a point/vector type of choice */ #ifndef RGFW_point typedef struct { i32 x, y; } RGFW_point; #endif - /* basic rect type, if there's not already a rect type of choice */ +/*! basic rect type, if there's not already a rect type of choice */ #ifndef RGFW_rect typedef struct { i32 x, y, w, h; } RGFW_rect; #endif - /* basic area type, if there's not already a area type of choice */ +/*! basic area type, if there's not already a area type of choice */ #ifndef RGFW_area typedef struct { u32 w, h; } RGFW_area; #endif -#define RGFW_POINT(x, y) (RGFW_point){x, y} -#define RGFW_RECT(x, y, w, h) (RGFW_rect){x, y, w, h} -#define RGFW_AREA(w, h) (RGFW_area){w, h} +#ifndef __cplusplus +#define RGFW_POINT(x, y) (RGFW_point){(i32)(x), (i32)(y)} +#define RGFW_RECT(x, y, w, h) (RGFW_rect){(i32)(x), (i32)(y), (i32)(w), (i32)(h)} +#define RGFW_AREA(w, h) (RGFW_area){(u32)(w), (u32)(h)} +#else +#define RGFW_POINT(x, y) {(i32)(x), (i32)(y)} +#define RGFW_RECT(x, y, w, h) {(i32)(x), (i32)(y), (i32)(w), (i32)(h)} +#define RGFW_AREA(w, h) {(u32)(w), (u32)(h)} +#endif #ifndef RGFW_NO_MONITOR + /*! structure for monitor data */ typedef struct RGFW_monitor { - char name[128]; /* monitor name */ - RGFW_rect rect; /* monitor Workarea */ - float scaleX, scaleY; /* monitor content scale*/ - float physW, physH; /* monitor physical size */ + char name[128]; /*!< monitor name */ + RGFW_rect rect; /*!< monitor Workarea */ + float scaleX, scaleY; /*!< monitor content scale*/ + float physW, physH; /*!< monitor physical size */ } RGFW_monitor; /* - NOTE : Monitor functions should be ran only as many times as needed (not in a loop) + NOTE : Monitor functions should be ran only as many times as needed (not in a loop) */ - /* get an array of all the monitors (max 6) */ + /*! get an array of all the monitors (max 6) */ RGFWDEF RGFW_monitor* RGFW_getMonitors(void); - /* get the primary monitor */ + /*! get the primary monitor */ RGFWDEF RGFW_monitor RGFW_getPrimaryMonitor(void); #endif /* NOTE: some parts of the data can represent different things based on the event (read comments in RGFW_Event struct) */ +/*! Event structure for checking/getting events */ typedef struct RGFW_Event { - char keyName[16]; /* key name of event*/ + char keyName[16]; /*!< key name of event*/ /*! drag and drop data */ /* 260 max paths with a max length of 260 */ @@ -452,67 +477,85 @@ typedef struct RGFW_Event { u32 type; /*!< which event has been sent?*/ RGFW_point point; /*!< mouse x, y of event (or drop point) */ - u32 fps; /*the current fps of the window [the fps is checked when events are checked]*/ - u64 frameTime, frameTime2; /* this is used for counting the fps */ + u8 keyCode; /*!< keycode of event !!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! */ - u8 keyCode; /*!< keycode of event !!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! */ - - b8 inFocus; /*if the window is in focus or not (this is always true for MacOS windows due to the api being weird) */ + b8 repeat; /*!< key press event repeated (the key is being held) */ + b8 inFocus; /*!< if the window is in focus or not (this is always true for MacOS windows due to the api being weird) */ u8 lockState; + + u8 button; /* !< which mouse button was pressed */ + double scroll; /*!< the raw mouse scroll value */ - u16 joystick; /* which joystick this event applies to (if applicable to any) */ - - u8 button; /*!< which mouse button has been clicked (0) left (1) middle (2) right OR which joystick button was pressed*/ - double scroll; /* the raw mouse scroll value */ - - u8 axisesCount; /* number of axises */ - RGFW_point axis[2]; /* x, y of axises (-100 to 100) */ -} RGFW_Event; /*!< Event structure for checking/getting events */ + u16 joystick; /*! which joystick this event applies to (if applicable to any) */ + u8 axisesCount; /*!< number of axises */ + RGFW_point axis[2]; /*!< x, y of axises (-100 to 100) */ -/* source data for the window (used by the APIs) */ + u64 frameTime, frameTime2; /*!< this is used for counting the fps */ +} RGFW_Event; +/*! source data for the window (used by the APIs) */ typedef struct RGFW_window_src { #ifdef RGFW_WINDOWS HWND window; /*!< source window */ HDC hdc; /*!< source HDC */ u32 hOffset; /*!< height offset for window */ -#if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL) + #if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL) HGLRC ctx; /*!< source graphics context */ -#elif defined(RGFW_OSMESA) + #elif defined(RGFW_OSMESA) OSMesaContext ctx; -#elif defined(RGFW_DIRECTX) + #elif defined(RGFW_DIRECTX) IDXGISwapChain* swapchain; ID3D11RenderTargetView* renderTargetView; ID3D11DepthStencilView* pDepthStencilView; -#elif defined(RGFW_EGL) + #elif defined(RGFW_EGL) EGLSurface EGL_surface; EGLDisplay EGL_display; EGLContext EGL_context; -#endif + #endif -#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) HDC hdcMem; HBITMAP bitmap; -#endif - RGFW_area maxSize, minSize; /* for setting max/min resize (RGFW_WINDOWS) */ + #endif + RGFW_area maxSize, minSize; /*!< for setting max/min resize (RGFW_WINDOWS) */ #elif defined(RGFW_X11) Display* display; /*!< source display */ Window window; /*!< source window */ -#if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL) + #if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL) GLXContext ctx; /*!< source graphics context */ -#elif defined(RGFW_OSMESA) + #elif defined(RGFW_OSMESA) OSMesaContext ctx; -#elif defined(RGFW_EGL) + #elif defined(RGFW_EGL) EGLSurface EGL_surface; EGLDisplay EGL_display; EGLContext EGL_context; -#endif + #endif #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) XImage* bitmap; GC gc; #endif +#elif defined(RGFW_WAYLAND) + struct wl_display* display; + struct wl_surface* surface; + struct wl_buffer* wl_buffer; + struct wl_keyboard* keyboard; + + struct xdg_surface* xdg_surface; + struct xdg_toplevel* xdg_toplevel; + struct zxdg_toplevel_decoration_v1* decoration; + RGFW_Event events[20]; + i32 eventLen; + size_t eventIndex; + #if defined(RGFW_EGL) + struct wl_egl_window* window; + EGLSurface EGL_surface; + EGLDisplay EGL_display; + EGLContext EGL_context; + #elif defined(RGFW_OSMESA) + OSMesaContext ctx; + #endif #elif defined(RGFW_MACOS) u32 display; void* displayLink; @@ -531,7 +574,7 @@ typedef struct RGFW_window_src { void* view; /*apple viewpoint thingy*/ #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - void* bitmap; /* API's bitmap for storing or managing */ + void* bitmap; /*!< API's bitmap for storing or managing */ void* image; #endif #elif defined(RGFW_WEBASM) @@ -542,33 +585,34 @@ typedef struct RGFW_window_src { typedef struct RGFW_window { - RGFW_window_src src; + RGFW_window_src src; /*!< src window data */ #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - u8* buffer; /* buffer for non-GPU systems (OSMesa, basic software rendering) */ + u8* buffer; /*!< buffer for non-GPU systems (OSMesa, basic software rendering) */ /* when rendering using RGFW_BUFFER, the buffer is in the RGBA format */ #endif - + void* userPtr; /* ptr for usr data */ + RGFW_Event event; /*!< current event */ - RGFW_rect r; /* the x, y, w and h of the struct */ + RGFW_rect r; /*!< the x, y, w and h of the struct */ - RGFW_point _lastMousePoint; /* last cusor point (for raw mouse data) */ - - u32 fpsCap; /*!< the fps cap of the window should run at (change this var to change the fps cap, 0 = no limit)*/ - /*[the fps is capped when events are checked]*/ - - u32 _winArgs; /* windows args (for RGFW to check) */ + RGFW_point _lastMousePoint; /*!< last cusor point (for raw mouse data) */ + + u32 _winArgs; /*!< windows args (for RGFW to check) */ } RGFW_window; /*!< Window structure for managing the window */ #if defined(RGFW_X11) || defined(RGFW_MACOS) - typedef u64 RGFW_thread; /* thread type unix */ + typedef u64 RGFW_thread; /*!< thread type unix */ #else - typedef void* RGFW_thread; /* thread type for window */ + typedef void* RGFW_thread; /*!< thread type for window */ #endif -/* this has to be set before createWindow is called, else the fulscreen size is used */ -RGFWDEF void RGFW_setBufferSize(RGFW_area size); /* the buffer cannot be resized (by RGFW) */ +/** * @defgroup Window_management +* @{ */ + +/*! this has to be set before createWindow is called, else the fulscreen size is used */ +RGFWDEF void RGFW_setBufferSize(RGFW_area size); /*!< the buffer cannot be resized (by RGFW) */ RGFW_window* RGFW_createWindow( const char* name, /* name of the window */ @@ -576,10 +620,10 @@ RGFW_window* RGFW_createWindow( u16 args /* extra arguments (NULL / (u16)0 means no args used)*/ ); /*!< function to create a window struct */ -/* get the size of the screen to an area struct */ +/*! get the size of the screen to an area struct */ RGFWDEF RGFW_area RGFW_getScreenSize(void); -/* +/*! this function checks an *individual* event (and updates window structure attributes) this means, using this function without a while loop may cause event lag @@ -593,7 +637,7 @@ RGFWDEF RGFW_area RGFW_getScreenSize(void); RGFW_Event* RGFW_window_checkEvent(RGFW_window* win); /*!< check current event (returns a pointer to win->event or NULL if there is no event)*/ -/* +/*! for RGFW_window_eventWait and RGFW_window_checkEvents waitMS -> Allows th e function to keep checking for events even after `RGFW_window_checkEvent == NULL` if waitMS == 0, the loop will not wait for events @@ -605,16 +649,16 @@ typedef RGFW_ENUM(i32, RGFW_eventWait) { RGFW_NO_WAIT = 0 }; -/* sleep until RGFW gets an event or the timer ends (defined by OS) */ +/*! sleep until RGFW gets an event or the timer ends (defined by OS) */ RGFWDEF void RGFW_window_eventWait(RGFW_window* win, i32 waitMS); -/* +/*! check all the events until there are none left, this should only be used if you're using callbacks only */ RGFWDEF void RGFW_window_checkEvents(RGFW_window* win, i32 waitMS); -/* +/*! Tell RGFW_window_eventWait to stop waiting, to be ran from another thread */ RGFWDEF void RGFW_stopCheckEvents(void); @@ -622,42 +666,42 @@ RGFWDEF void RGFW_stopCheckEvents(void); /*! window managment functions*/ RGFWDEF void RGFW_window_close(RGFW_window* win); /*!< close the window and free leftover data */ -/* moves window to a given point */ +/*! moves window to a given point */ RGFWDEF void RGFW_window_move(RGFW_window* win, - RGFW_point v/* new pos*/ + RGFW_point v/*!< new pos*/ ); #ifndef RGFW_NO_MONITOR - /* move to a specific monitor */ + /*! move to a specific monitor */ RGFWDEF void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m /* monitor */); #endif -/* resize window to a current size/area */ -RGFWDEF void RGFW_window_resize(RGFW_window* win, - RGFW_area a/* new size*/ +/*! resize window to a current size/area */ +RGFWDEF void RGFW_window_resize(RGFW_window* win, /*!< source window */ + RGFW_area a/*!< new size*/ ); -/* set the minimum size a user can shrink a window to a given size/area */ +/*! set the minimum size a user can shrink a window to a given size/area */ RGFWDEF void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a); -/* set the minimum size a user can extend a window to a given size/area */ +/*! set the minimum size a user can extend a window to a given size/area */ RGFWDEF void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a); -RGFWDEF void RGFW_window_maximize(RGFW_window* win); /* maximize the window size */ -RGFWDEF void RGFW_window_minimize(RGFW_window* win); /* minimize the window (in taskbar (per OS))*/ -RGFWDEF void RGFW_window_restore(RGFW_window* win); /* restore the window from minimized (per OS)*/ +RGFWDEF void RGFW_window_maximize(RGFW_window* win); /*!< maximize the window size */ +RGFWDEF void RGFW_window_minimize(RGFW_window* win); /*!< minimize the window (in taskbar (per OS))*/ +RGFWDEF void RGFW_window_restore(RGFW_window* win); /*!< restore the window from minimized (per OS)*/ -/* if the window should have a border or not (borderless) based on bool value of `border` */ +/*! if the window should have a border or not (borderless) based on bool value of `border` */ RGFWDEF void RGFW_window_setBorder(RGFW_window* win, b8 border); -/* turn on / off dnd (RGFW_ALLOW_DND stil must be passed to the window)*/ +/*! turn on / off dnd (RGFW_ALLOW_DND stil must be passed to the window)*/ RGFWDEF void RGFW_window_setDND(RGFW_window* win, b8 allow); #ifndef RGFW_NO_PASSTHROUGH - /* turn on / off mouse passthrough */ + /*!! turn on / off mouse passthrough */ RGFWDEF void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough); #endif -/* rename window to a given string */ +/*! rename window to a given string */ RGFWDEF void RGFW_window_setName(RGFW_window* win, char* name ); @@ -674,7 +718,7 @@ RGFWDEF void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 /*!< sets the mouse to a standard API cursor (based on RGFW_MOUSE, as seen at the end of the RGFW_HEADER part of this file) */ RGFWDEF void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse); -RGFWDEF void RGFW_window_setMouseDefault(RGFW_window* win); /* sets the mouse to the default mouse icon */ +RGFWDEF void RGFW_window_setMouseDefault(RGFW_window* win); /*!< sets the mouse to the default mouse icon */ /* Locks cursor at the center of the window win->event.point become raw mouse movement data @@ -682,12 +726,12 @@ RGFWDEF void RGFW_window_setMouseDefault(RGFW_window* win); /* sets the mouse to this is useful for a 3D camera */ RGFWDEF void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area); -/* stop holding the mouse and let it move freely */ +/*! stop holding the mouse and let it move freely */ RGFWDEF void RGFW_window_mouseUnhold(RGFW_window* win); -/* hide the window */ +/*! hide the window */ RGFWDEF void RGFW_window_hide(RGFW_window* win); -/* show the window */ +/*! show the window */ RGFWDEF void RGFW_window_show(RGFW_window* win); /* @@ -696,28 +740,32 @@ RGFWDEF void RGFW_window_show(RGFW_window* win); */ RGFWDEF void RGFW_window_setShouldClose(RGFW_window* win); -/* where the mouse is on the screen */ +/*! where the mouse is on the screen */ RGFWDEF RGFW_point RGFW_getGlobalMousePoint(void); -/* where the mouse is on the window */ +/*! where the mouse is on the window */ RGFWDEF RGFW_point RGFW_window_getMousePoint(RGFW_window* win); -/* show the mouse or hide the mouse*/ +/*! show the mouse or hide the mouse*/ RGFWDEF void RGFW_window_showMouse(RGFW_window* win, i8 show); -/* move the mouse to a set x, y pos*/ +/*! move the mouse to a set x, y pos*/ RGFWDEF void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v); -/* if the window should close (RGFW_close was sent or escape was pressed) */ +/*! if the window should close (RGFW_close was sent or escape was pressed) */ RGFWDEF b8 RGFW_window_shouldClose(RGFW_window* win); -/* if window is fullscreen'd */ +/*! if window is fullscreen'd */ RGFWDEF b8 RGFW_window_isFullscreen(RGFW_window* win); -/* if window is hidden */ +/*! if window is hidden */ RGFWDEF b8 RGFW_window_isHidden(RGFW_window* win); -/* if window is minimized */ +/*! if window is minimized */ RGFWDEF b8 RGFW_window_isMinimized(RGFW_window* win); -/* if window is maximized */ +/*! if window is maximized */ RGFWDEF b8 RGFW_window_isMaximized(RGFW_window* win); +/** @} */ + +/** * @defgroup Monitor +* @{ */ #ifndef RGFW_NO_MONITOR /* @@ -725,17 +773,27 @@ scale the window to the monitor, this is run by default if the user uses the arg `RGFW_SCALE_TO_MONITOR` during window creation */ RGFWDEF void RGFW_window_scaleToMonitor(RGFW_window* win); -/* get the struct of the window's monitor */ +/*! get the struct of the window's monitor */ RGFWDEF RGFW_monitor RGFW_window_getMonitor(RGFW_window* win); #endif -/*!< make the window the current opengl drawing context */ -RGFWDEF void RGFW_window_makeCurrent(RGFW_window* win); +/** @} */ + +/** * @defgroup Input +* @{ */ /*error handling*/ -RGFWDEF b8 RGFW_Error(void); /* returns true if an error has occurred (doesn't print errors itself) */ +RGFWDEF b8 RGFW_Error(void); /*!< returns true if an error has occurred (doesn't print errors itself) */ + +/*! returns true if the key should be shifted */ +RGFWDEF b8 RGFW_shouldShift(u32 keycode, u8 lockState); + +/*! get char from RGFW keycode (using a LUT), uses shift'd version if shift = true */ +RGFWDEF char RGFW_keyCodeToChar(u32 keycode, b8 shift); +/*! get char from RGFW keycode (using a LUT), uses lockState for shouldShift) */ +RGFWDEF char RGFW_keyCodeToCharAuto(u32 keycode, u8 lockState); -/*!< if window == NULL, it checks if the key is pressed globally. Otherwise, it checks only if the key is pressed while the window in focus.*/ +/*! if window == NULL, it checks if the key is pressed globally. Otherwise, it checks only if the key is pressed while the window in focus.*/ RGFWDEF b8 RGFW_isPressed(RGFW_window* win, u8 key); /*!< if key is pressed (key code)*/ RGFWDEF b8 RGFW_wasPressed(RGFW_window* win, u8 key); /*!< if key was pressed (checks previous state only) (key code)*/ @@ -744,90 +802,100 @@ RGFWDEF b8 RGFW_isHeld(RGFW_window* win, u8 key); /*!< if key is held (key code) RGFWDEF b8 RGFW_isReleased(RGFW_window* win, u8 key); /*!< if key is released (key code)*/ /* if a key is pressed and then released, pretty much the same as RGFW_isReleased */ -RGFWDEF b8 RGFW_isClicked(RGFW_window* win, u8 key /* key code*/); - -/* if a mouse button is pressed */ -RGFWDEF b8 RGFW_isMousePressed(RGFW_window* win, u8 button /* mouse button code */ ); -/* if a mouse button is held */ -RGFWDEF b8 RGFW_isMouseHeld(RGFW_window* win, u8 button /* mouse button code */ ); -/* if a mouse button was released */ -RGFWDEF b8 RGFW_isMouseReleased(RGFW_window* win, u8 button /* mouse button code */ ); -/* if a mouse button was pressed (checks previous state only) */ -RGFWDEF b8 RGFW_wasMousePressed(RGFW_window* win, u8 button /* mouse button code */ ); - -/*! clipboard functions*/ +RGFWDEF b8 RGFW_isClicked(RGFW_window* win, u8 key /*!< key code*/); + +/*! if a mouse button is pressed */ +RGFWDEF b8 RGFW_isMousePressed(RGFW_window* win, u8 button /*!< mouse button code */ ); +/*! if a mouse button is held */ +RGFWDEF b8 RGFW_isMouseHeld(RGFW_window* win, u8 button /*!< mouse button code */ ); +/*! if a mouse button was released */ +RGFWDEF b8 RGFW_isMouseReleased(RGFW_window* win, u8 button /*!< mouse button code */ ); +/*! if a mouse button was pressed (checks previous state only) */ +RGFWDEF b8 RGFW_wasMousePressed(RGFW_window* win, u8 button /*!< mouse button code */ ); +/** @} */ + +/** * @defgroup Clipboard +* @{ */ RGFWDEF char* RGFW_readClipboard(size_t* size); /*!< read clipboard data */ -RGFWDEF void RGFW_clipboardFree(char* str); /* the string returned from RGFW_readClipboard must be freed */ +RGFWDEF void RGFW_clipboardFree(char* str); /*!< the string returned from RGFW_readClipboard must be freed */ RGFWDEF void RGFW_writeClipboard(const char* text, u32 textLen); /*!< write text to the clipboard */ +/** @} */ -/* +/** Event callbacks, these are completely optional, you can use the normal RGFW_checkEvent() method if you prefer that +* @defgroup Callbacks +* @{ */ -/* RGFW_windowMoved, the window and its new rect value */ +/*! RGFW_windowMoved, the window and its new rect value */ typedef void (* RGFW_windowmovefunc)(RGFW_window* win, RGFW_rect r); -/* RGFW_windowResized, the window and its new rect value */ +/*! RGFW_windowResized, the window and its new rect value */ typedef void (* RGFW_windowresizefunc)(RGFW_window* win, RGFW_rect r); -/* RGFW_quit, the window that was closed */ +/*! RGFW_quit, the window that was closed */ typedef void (* RGFW_windowquitfunc)(RGFW_window* win); -/* RGFW_focusIn / RGFW_focusOut, the window who's focus has changed and if its inFocus */ +/*! RGFW_focusIn / RGFW_focusOut, the window who's focus has changed and if its inFocus */ typedef void (* RGFW_focusfunc)(RGFW_window* win, b8 inFocus); -/* RGFW_mouseEnter / RGFW_mouseLeave, the window that changed, the point of the mouse (enter only) and if the mouse has entered */ +/*! RGFW_mouseEnter / RGFW_mouseLeave, the window that changed, the point of the mouse (enter only) and if the mouse has entered */ typedef void (* RGFW_mouseNotifyfunc)(RGFW_window* win, RGFW_point point, b8 status); -/* RGFW_mousePosChanged, the window that the move happened on and the new point of the mouse */ +/*! RGFW_mousePosChanged, the window that the move happened on and the new point of the mouse */ typedef void (* RGFW_mouseposfunc)(RGFW_window* win, RGFW_point point); -/* RGFW_dnd_init, the window, the point of the drop on the windows */ +/*! RGFW_dnd_init, the window, the point of the drop on the windows */ typedef void (* RGFW_dndInitfunc)(RGFW_window* win, RGFW_point point); -/* RGFW_windowRefresh, the window that needs to be refreshed */ +/*! RGFW_windowRefresh, the window that needs to be refreshed */ typedef void (* RGFW_windowrefreshfunc)(RGFW_window* win); -/* RGFW_keyPressed / RGFW_keyReleased, the window that got the event, the keycode, the string version, the state of mod keys, if it was a press (else it's a release) */ +/*! RGFW_keyPressed / RGFW_keyReleased, the window that got the event, the keycode, the string version, the state of mod keys, if it was a press (else it's a release) */ typedef void (* RGFW_keyfunc)(RGFW_window* win, u32 keycode, char keyName[16], u8 lockState, b8 pressed); -/* RGFW_mouseButtonPressed / RGFW_mouseButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */ +/*! RGFW_mouseButtonPressed / RGFW_mouseButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */ typedef void (* RGFW_mousebuttonfunc)(RGFW_window* win, u8 button, double scroll, b8 pressed); -/* RGFW_jsButtonPressed / RGFW_jsButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */ +/*! RGFW_jsButtonPressed / RGFW_jsButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */ typedef void (* RGFW_jsButtonfunc)(RGFW_window* win, u16 joystick, u8 button, b8 pressed); -/* RGFW_jsAxisMove, the window that got the event, the joystick in question, the axis values and the amount of axises */ +/*! RGFW_jsAxisMove, the window that got the event, the joystick in question, the axis values and the amount of axises */ typedef void (* RGFW_jsAxisfunc)(RGFW_window* win, u16 joystick, RGFW_point axis[2], u8 axisesCount); -/* RGFW_dnd, the window that had the drop, the drop data and the amount files dropped */ + +/*! RGFW_dnd, the window that had the drop, the drop data and the amount files dropped returns previous callback function (if it was set) */ #ifdef RGFW_ALLOC_DROPFILES typedef void (* RGFW_dndfunc)(RGFW_window* win, char** droppedFiles, u32 droppedFilesCount); #else typedef void (* RGFW_dndfunc)(RGFW_window* win, char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH], u32 droppedFilesCount); #endif -/* set callback for a window move event */ -RGFWDEF void RGFW_setWindowMoveCallback(RGFW_windowmovefunc func); -/* set callbacksfor a window resize event */ -RGFWDEF void RGFW_setWindowResizeCallback(RGFW_windowresizefunc func); -/* set callbacksfor a window quit event */ -RGFWDEF void RGFW_setWindowQuitCallback(RGFW_windowquitfunc func); -/* set callbacksfor a mouse move event */ -RGFWDEF void RGFW_setMousePosCallback(RGFW_mouseposfunc func); -/* set callbacksfor a window refresh event */ -RGFWDEF void RGFW_setWindowRefreshCallback(RGFW_windowrefreshfunc func); -/* set callbacksfor a window focus change event */ -RGFWDEF void RGFW_setFocusCallback(RGFW_focusfunc func); -/* set callbacksfor a mouse notify event */ -RGFWDEF void RGFW_setMouseNotifyCallBack(RGFW_mouseNotifyfunc func); -/* set callbacksfor a drop event event */ -RGFWDEF void RGFW_setDndCallback(RGFW_dndfunc func); -/* set callbacksfor a start of a drop event */ -RGFWDEF void RGFW_setDndInitCallback(RGFW_dndInitfunc func); -/* set callbacksfor a key (press / release ) event */ -RGFWDEF void RGFW_setKeyCallback(RGFW_keyfunc func); -/* set callbacksfor a mouse button (press / release ) event */ -RGFWDEF void RGFW_setMouseButtonCallback(RGFW_mousebuttonfunc func); -/* set callbacksfor a controller button (press / release ) event */ -RGFWDEF void RGFW_setjsButtonCallback(RGFW_jsButtonfunc func); -/* set callbacksfor a joystick axis mov event */ -RGFWDEF void RGFW_setjsAxisCallback(RGFW_jsAxisfunc func); - +/*! set callback for a window move event returns previous callback function (if it was set) */ +RGFWDEF RGFW_windowmovefunc RGFW_setWindowMoveCallback(RGFW_windowmovefunc func); +/*! set callback for a window resize event returns previous callback function (if it was set) */ +RGFWDEF RGFW_windowresizefunc RGFW_setWindowResizeCallback(RGFW_windowresizefunc func); +/*! set callback for a window quit event returns previous callback function (if it was set) */ +RGFWDEF RGFW_windowquitfunc RGFW_setWindowQuitCallback(RGFW_windowquitfunc func); +/*! set callback for a mouse move event returns previous callback function (if it was set) */ +RGFWDEF RGFW_mouseposfunc RGFW_setMousePosCallback(RGFW_mouseposfunc func); +/*! set callback for a window refresh event returns previous callback function (if it was set) */ +RGFWDEF RGFW_windowrefreshfunc RGFW_setWindowRefreshCallback(RGFW_windowrefreshfunc func); +/*! set callback for a window focus change event returns previous callback function (if it was set) */ +RGFWDEF RGFW_focusfunc RGFW_setFocusCallback(RGFW_focusfunc func); +/*! set callback for a mouse notify event returns previous callback function (if it was set) */ +RGFWDEF RGFW_mouseNotifyfunc RGFW_setMouseNotifyCallBack(RGFW_mouseNotifyfunc func); +/*! set callback for a drop event event returns previous callback function (if it was set) */ +RGFWDEF RGFW_dndfunc RGFW_setDndCallback(RGFW_dndfunc func); +/*! set callback for a start of a drop event returns previous callback function (if it was set) */ +RGFWDEF RGFW_dndInitfunc RGFW_setDndInitCallback(RGFW_dndInitfunc func); +/*! set callback for a key (press / release ) event returns previous callback function (if it was set) */ +RGFWDEF RGFW_keyfunc RGFW_setKeyCallback(RGFW_keyfunc func); +/*! set callback for a mouse button (press / release ) event returns previous callback function (if it was set) */ +RGFWDEF RGFW_mousebuttonfunc RGFW_setMouseButtonCallback(RGFW_mousebuttonfunc func); +/*! set callback for a controller button (press / release ) event returns previous callback function (if it was set) */ +RGFWDEF RGFW_jsButtonfunc RGFW_setjsButtonCallback(RGFW_jsButtonfunc func); +/*! set callback for a joystick axis mov event returns previous callback function (if it was set) */ +RGFWDEF RGFW_jsAxisfunc RGFW_setjsAxisCallback(RGFW_jsAxisfunc func); + +/** @} */ + +/** * @defgroup Threads +* @{ */ #ifndef RGFW_NO_THREADS /*! threading functions*/ @@ -851,7 +919,10 @@ RGFWDEF void RGFW_setjsAxisCallback(RGFW_jsAxisfunc func); RGFWDEF void RGFW_setThreadPriority(RGFW_thread thread, u8 priority); /*!< sets the priority priority */ #endif -/*! gamepad/joystick functions (linux-only currently) */ +/** @} */ + +/** * @defgroup joystick +* @{ */ /*! joystick count starts at 0*/ /*!< register joystick to window based on a number (the number is based on when it was connected eg. /dev/js0)*/ @@ -860,27 +931,45 @@ RGFWDEF u16 RGFW_registerJoystickF(RGFW_window* win, char* file); RGFWDEF u32 RGFW_isPressedJS(RGFW_window* win, u16 controller, u8 button); +/** @} */ + +/** * @defgroup graphics_API +* @{ */ + +/*!< make the window the current opengl drawing context + + NOTE: + if you want to switch the graphics context's thread, + you have to run RGFW_window_makeCurrent(NULL); on the old thread + then RGFW_window_makeCurrent(valid_window) on the new thread +*/ +RGFWDEF void RGFW_window_makeCurrent(RGFW_window* win); + +/*< updates fps / sets fps to cap (must by ran manually by the user at the end of a frame), returns current fps */ +RGFWDEF u32 RGFW_window_checkFPS(RGFW_window* win, u32 fpsCap); + /* supports openGL, directX, OSMesa, EGL and software rendering */ -RGFWDEF void RGFW_window_swapBuffers(RGFW_window* win); /* swap the rendering buffer */ +RGFWDEF void RGFW_window_swapBuffers(RGFW_window* win); /*!< swap the rendering buffer */ RGFWDEF void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval); RGFWDEF void RGFW_window_setGPURender(RGFW_window* win, i8 set); RGFWDEF void RGFW_window_setCPURender(RGFW_window* win, i8 set); /*! native API functions */ -#ifdef RGFW_OPENGL - /*! Get max OpenGL version */ - RGFWDEF u8* RGFW_getMaxGLVersion(void); - /* OpenGL init hints */ - RGFWDEF void RGFW_setGLStencil(i32 stencil); /* set stencil buffer bit size (8 by default) */ - RGFWDEF void RGFW_setGLSamples(i32 samples); /* set number of sampiling buffers (4 by default) */ - RGFWDEF void RGFW_setGLStereo(i32 stereo); /* use GL_STEREO (GL_FALSE by default) */ - RGFWDEF void RGFW_setGLAuxBuffers(i32 auxBuffers); /* number of aux buffers (0 by default) */ - - /*! Set OpenGL version hint */ - RGFWDEF void RGFW_setGLVersion(i32 major, i32 minor); - RGFWDEF void* RGFW_getProcAddress(const char* procname); /* get native opengl proc address */ - RGFWDEF void RGFW_window_makeCurrent_OpenGL(RGFW_window* win); /* to be called by RGFW_window_makeCurrent */ +#if defined(RGFW_OPENGL) || defined(RGFW_EGL) + /*! OpenGL init hints */ + RGFWDEF void RGFW_setGLStencil(i32 stencil); /*!< set stencil buffer bit size (8 by default) */ + RGFWDEF void RGFW_setGLSamples(i32 samples); /*!< set number of sampiling buffers (4 by default) */ + RGFWDEF void RGFW_setGLStereo(i32 stereo); /*!< use GL_STEREO (GL_FALSE by default) */ + RGFWDEF void RGFW_setGLAuxBuffers(i32 auxBuffers); /*!< number of aux buffers (0 by default) */ + + /*! which profile to use for the opengl verion */ + typedef RGFW_ENUM(u8, RGFW_GL_profile) { RGFW_GL_CORE = 0, RGFW_GL_COMPATIBILITY }; + /*! Set OpenGL version hint (core or compatibility profile)*/ + RGFWDEF void RGFW_setGLVersion(RGFW_GL_profile profile, i32 major, i32 minor); + RGFWDEF void RGFW_setDoubleBuffer(b8 useDoubleBuffer); + RGFWDEF void* RGFW_getProcAddress(const char* procname); /*!< get native opengl proc address */ + RGFWDEF void RGFW_window_makeCurrent_OpenGL(RGFW_window* win); /*!< to be called by RGFW_window_makeCurrent */ #elif defined(RGFW_DIRECTX) typedef struct { IDXGIFactory* pFactory; @@ -896,13 +985,15 @@ RGFWDEF void RGFW_window_setCPURender(RGFW_window* win, i8 set); RGFWDEF RGFW_directXinfo* RGFW_getDirectXInfo(void); #endif -/*! Supporting functions */ -RGFWDEF void RGFW_window_checkFPS(RGFW_window* win); /*!< updates fps / sets fps to cap (ran by RGFW_window_checkEvent)*/ -RGFWDEF u64 RGFW_getTime(void); /* get time in seconds */ -RGFWDEF u64 RGFW_getTimeNS(void); /* get time in nanoseconds */ -RGFWDEF void RGFW_sleep(u64 milisecond); /* sleep for a set time */ +/** @} */ -/* +/** * @defgroup Supporting +* @{ */ +RGFWDEF u64 RGFW_getTime(void); /*!< get time in seconds */ +RGFWDEF u64 RGFW_getTimeNS(void); /*!< get time in nanoseconds */ +RGFWDEF void RGFW_sleep(u64 milisecond); /*!< sleep for a set time */ + +/*! key codes and mouse icon enums */ @@ -1019,6 +1110,7 @@ typedef RGFW_ENUM(u8, RGFW_Key) { final_key, }; + typedef RGFW_ENUM(u8, RGFW_mouseIcons) { RGFW_MOUSE_NORMAL = 0, RGFW_MOUSE_ARROW, @@ -1033,6 +1125,8 @@ typedef RGFW_ENUM(u8, RGFW_mouseIcons) { RGFW_MOUSE_NOT_ALLOWED, }; +/** @} */ + #endif /* RGFW_HEADER */ /* @@ -1092,15 +1186,18 @@ int main() { */ #ifdef RGFW_X11 - #define RGFW_OS_BASED_VALUE(l, w, m, a) l + #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) l #elif defined(RGFW_WINDOWS) - #define RGFW_OS_BASED_VALUE(l, w, m, a) w + #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) w #elif defined(RGFW_MACOS) - #define RGFW_OS_BASED_VALUE(l, w, m, a) m + #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) m #elif defined(RGFW_WEBASM) - #define RGFW_OS_BASED_VALUE(l, w, m, a) a + #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) h +#elif defined(RGFW_WAYLAND) + #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) ww #endif + #ifdef RGFW_IMPLEMENTATION #include @@ -1119,120 +1216,147 @@ This is the start of keycode data MacOS -> windows and linux already don't have keycodes as macros, so there's no point */ -u8 RGFW_keycodes[] = { - [RGFW_OS_BASED_VALUE(49, 192, 50, DOM_VK_BACK_QUOTE)] = RGFW_Backtick, - - [RGFW_OS_BASED_VALUE(19, 0x30, 29, DOM_VK_0)] = RGFW_0, - [RGFW_OS_BASED_VALUE(10, 0x31, 18, DOM_VK_1)] = RGFW_1, - [RGFW_OS_BASED_VALUE(11, 0x32, 19, DOM_VK_2)] = RGFW_2, - [RGFW_OS_BASED_VALUE(12, 0x33, 20, DOM_VK_3)] = RGFW_3, - [RGFW_OS_BASED_VALUE(13, 0x34, 21, DOM_VK_4)] = RGFW_4, - [RGFW_OS_BASED_VALUE(14, 0x35, 23, DOM_VK_5)] = RGFW_5, - [RGFW_OS_BASED_VALUE(15, 0x36, 22, DOM_VK_6)] = RGFW_6, - [RGFW_OS_BASED_VALUE(16, 0x37, 26, DOM_VK_7)] = RGFW_7, - [RGFW_OS_BASED_VALUE(17, 0x38, 28, DOM_VK_8)] = RGFW_8, - [RGFW_OS_BASED_VALUE(18, 0x39, 25, DOM_VK_9)] = RGFW_9, - - [RGFW_OS_BASED_VALUE(65, 0x20, 49, DOM_VK_SPACE)] = RGFW_Space, - - [RGFW_OS_BASED_VALUE(38, 0x41, 0, DOM_VK_A)] = RGFW_a, - [RGFW_OS_BASED_VALUE(56, 0x42, 11, DOM_VK_B)] = RGFW_b, - [RGFW_OS_BASED_VALUE(54, 0x43, 8, DOM_VK_C)] = RGFW_c, - [RGFW_OS_BASED_VALUE(40, 0x44, 2, DOM_VK_D)] = RGFW_d, - [RGFW_OS_BASED_VALUE(26, 0x45, 14, DOM_VK_E)] = RGFW_e, - [RGFW_OS_BASED_VALUE(41, 0x46, 3, DOM_VK_F)] = RGFW_f, - [RGFW_OS_BASED_VALUE(42, 0x47, 5, DOM_VK_G)] = RGFW_g, - [RGFW_OS_BASED_VALUE(43, 0x48, 4, DOM_VK_H)] = RGFW_h, - [RGFW_OS_BASED_VALUE(31, 0x49, 34, DOM_VK_I)] = RGFW_i, - [RGFW_OS_BASED_VALUE(44, 0x4A, 38, DOM_VK_J)] = RGFW_j, - [RGFW_OS_BASED_VALUE(45, 0x4B, 40, DOM_VK_K)] = RGFW_k, - [RGFW_OS_BASED_VALUE(46, 0x4C, 37, DOM_VK_L)] = RGFW_l, - [RGFW_OS_BASED_VALUE(58, 0x4D, 46, DOM_VK_M)] = RGFW_m, - [RGFW_OS_BASED_VALUE(57, 0x4E, 45, DOM_VK_N)] = RGFW_n, - [RGFW_OS_BASED_VALUE(32, 0x4F, 31, DOM_VK_O)] = RGFW_o, - [RGFW_OS_BASED_VALUE(33, 0x50, 35, DOM_VK_P)] = RGFW_p, - [RGFW_OS_BASED_VALUE(24, 0x51, 12, DOM_VK_Q)] = RGFW_q, - [RGFW_OS_BASED_VALUE(27, 0x52, 15, DOM_VK_R)] = RGFW_r, - [RGFW_OS_BASED_VALUE(39, 0x53, 1, DOM_VK_S)] = RGFW_s, - [RGFW_OS_BASED_VALUE(28, 0x54, 17, DOM_VK_T)] = RGFW_t, - [RGFW_OS_BASED_VALUE(30, 0x55, 32, DOM_VK_U)] = RGFW_u, - [RGFW_OS_BASED_VALUE(55, 0x56, 9, DOM_VK_V)] = RGFW_v, - [RGFW_OS_BASED_VALUE(25, 0x57, 13, DOM_VK_W)] = RGFW_w, - [RGFW_OS_BASED_VALUE(53, 0x58, 7, DOM_VK_X)] = RGFW_x, - [RGFW_OS_BASED_VALUE(29, 0x59, 16, DOM_VK_Y)] = RGFW_y, - [RGFW_OS_BASED_VALUE(52, 0x5A, 6, DOM_VK_Z)] = RGFW_z, - - [RGFW_OS_BASED_VALUE(60, 190, 47, DOM_VK_PERIOD)] = RGFW_Period, - [RGFW_OS_BASED_VALUE(59, 188, 43, DOM_VK_COMMA)] = RGFW_Comma, - [RGFW_OS_BASED_VALUE(61, 191, 44, DOM_VK_SLASH)] = RGFW_Slash, - [RGFW_OS_BASED_VALUE(34, 219, 33, DOM_VK_OPEN_BRACKET)] = RGFW_Bracket, - [RGFW_OS_BASED_VALUE(35, 221, 30, DOM_VK_CLOSE_BRACKET)] = RGFW_CloseBracket, - [RGFW_OS_BASED_VALUE(47, 186, 41, DOM_VK_SEMICOLON)] = RGFW_Semicolon, - [RGFW_OS_BASED_VALUE(48, 222, 39, DOM_VK_QUOTE)] = RGFW_Quote, - [RGFW_OS_BASED_VALUE(51, 322, 42, DOM_VK_BACK_SLASH)] = RGFW_BackSlash, + + +/* + the c++ compiler doesn't support setting up an array like, + we'll have to do it during runtime using a function & this messy setup +*/ +#ifndef __cplusplus +#define RGFW_NEXT , +#define RGFW_MAP +#else +#define RGFW_NEXT ; +#define RGFW_MAP RGFW_keycodes +#endif + +#ifdef RGFW_WAYLAND +#include +#endif + +u8 RGFW_keycodes [RGFW_OS_BASED_VALUE(136, 337, 128, DOM_VK_WIN_OEM_CLEAR + 1, 130)] = { +#ifdef __cplusplus + 0 +}; +void RGFW_init_keys(void) { +#endif + RGFW_MAP [RGFW_OS_BASED_VALUE(49, 192, 50, DOM_VK_BACK_QUOTE, KEY_GRAVE)] = RGFW_Backtick RGFW_NEXT + + RGFW_MAP [RGFW_OS_BASED_VALUE(19, 0x30, 29, DOM_VK_0, KEY_0)] = RGFW_0 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(10, 0x31, 18, DOM_VK_1, KEY_1)] = RGFW_1 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(11, 0x32, 19, DOM_VK_2, KEY_2)] = RGFW_2 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(12, 0x33, 20, DOM_VK_3, KEY_3)] = RGFW_3 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(13, 0x34, 21, DOM_VK_4, KEY_4)] = RGFW_4 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(14, 0x35, 23, DOM_VK_5, KEY_5)] = RGFW_5 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(15, 0x36, 22, DOM_VK_6, KEY_6)] = RGFW_6 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(16, 0x37, 26, DOM_VK_7, KEY_7)] = RGFW_7 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(17, 0x38, 28, DOM_VK_8, KEY_8)] = RGFW_8 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(18, 0x39, 25, DOM_VK_9, KEY_9)] = RGFW_9, + + RGFW_MAP [RGFW_OS_BASED_VALUE(65, 0x20, 49, DOM_VK_SPACE, KEY_SPACE)] = RGFW_Space, + + RGFW_MAP [RGFW_OS_BASED_VALUE(38, 0x41, 0, DOM_VK_A, KEY_A)] = RGFW_a RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(56, 0x42, 11, DOM_VK_B, KEY_B)] = RGFW_b RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(54, 0x43, 8, DOM_VK_C, KEY_C)] = RGFW_c RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(40, 0x44, 2, DOM_VK_D, KEY_D)] = RGFW_d RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(26, 0x45, 14, DOM_VK_E, KEY_E)] = RGFW_e RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(41, 0x46, 3, DOM_VK_F, KEY_F)] = RGFW_f RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(42, 0x47, 5, DOM_VK_G, KEY_G)] = RGFW_g RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(43, 0x48, 4, DOM_VK_H, KEY_H)] = RGFW_h RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(31, 0x49, 34, DOM_VK_I, KEY_I)] = RGFW_i RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(44, 0x4A, 38, DOM_VK_J, KEY_J)] = RGFW_j RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(45, 0x4B, 40, DOM_VK_K, KEY_K)] = RGFW_k RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(46, 0x4C, 37, DOM_VK_L, KEY_L)] = RGFW_l RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(58, 0x4D, 46, DOM_VK_M, KEY_M)] = RGFW_m RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(57, 0x4E, 45, DOM_VK_N, KEY_N)] = RGFW_n RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(32, 0x4F, 31, DOM_VK_O, KEY_O)] = RGFW_o RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(33, 0x50, 35, DOM_VK_P, KEY_P)] = RGFW_p RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(24, 0x51, 12, DOM_VK_Q, KEY_Q)] = RGFW_q RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(27, 0x52, 15, DOM_VK_R, KEY_R)] = RGFW_r RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(39, 0x53, 1, DOM_VK_S, KEY_S)] = RGFW_s RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(28, 0x54, 17, DOM_VK_T, KEY_T)] = RGFW_t RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(30, 0x55, 32, DOM_VK_U, KEY_U)] = RGFW_u RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(55, 0x56, 9, DOM_VK_V, KEY_V)] = RGFW_v RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(25, 0x57, 13, DOM_VK_W, KEY_W)] = RGFW_w RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(53, 0x58, 7, DOM_VK_X, KEY_X)] = RGFW_x RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(29, 0x59, 16, DOM_VK_Y, KEY_Y)] = RGFW_y RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(52, 0x5A, 6, DOM_VK_Z, KEY_Z)] = RGFW_z, + + RGFW_MAP [RGFW_OS_BASED_VALUE(60, 190, 47, DOM_VK_PERIOD, KEY_DOT)] = RGFW_Period RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(59, 188, 43, DOM_VK_COMMA, KEY_COMMA)] = RGFW_Comma RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(61, 191, 44, DOM_VK_SLASH, KEY_SLASH)] = RGFW_Slash RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(34, 219, 33, DOM_VK_OPEN_BRACKET, KEY_LEFTBRACE)] = RGFW_Bracket RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(35, 221, 30, DOM_VK_CLOSE_BRACKET, KEY_RIGHTBRACE)] = RGFW_CloseBracket RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(47, 186, 41, DOM_VK_SEMICOLON, KEY_SEMICOLON)] = RGFW_Semicolon RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(48, 222, 39, DOM_VK_QUOTE, KEY_APOSTROPHE)] = RGFW_Quote RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(51, 322, 42, DOM_VK_BACK_SLASH, KEY_BACKSLASH)] = RGFW_BackSlash, - [RGFW_OS_BASED_VALUE(36, 0x0D, 36, DOM_VK_RETURN)] = RGFW_Return, - [RGFW_OS_BASED_VALUE(119, 0x2E, 118, DOM_VK_DELETE)] = RGFW_Delete, - [RGFW_OS_BASED_VALUE(77, 0x90, 72, DOM_VK_NUM_LOCK)] = RGFW_Numlock, - [RGFW_OS_BASED_VALUE(106, 0x6F, 82, DOM_VK_DIVIDE)] = RGFW_KP_Slash, - [RGFW_OS_BASED_VALUE(63, 0x6A, 76, DOM_VK_MULTIPLY)] = RGFW_Multiply, - [RGFW_OS_BASED_VALUE(82, 0x6D, 67, DOM_VK_SUBTRACT)] = RGFW_KP_Minus, - [RGFW_OS_BASED_VALUE(87, 0x61, 84, DOM_VK_NUMPAD1)] = RGFW_KP_1, - [RGFW_OS_BASED_VALUE(88, 0x62, 85, DOM_VK_NUMPAD2)] = RGFW_KP_2, - [RGFW_OS_BASED_VALUE(89, 0x63, 86, DOM_VK_NUMPAD3)] = RGFW_KP_3, - [RGFW_OS_BASED_VALUE(83, 0x64, 87, DOM_VK_NUMPAD4)] = RGFW_KP_4, - [RGFW_OS_BASED_VALUE(84, 0x65, 88, DOM_VK_NUMPAD5)] = RGFW_KP_5, - [RGFW_OS_BASED_VALUE(85, 0x66, 89, DOM_VK_NUMPAD6)] = RGFW_KP_6, - [RGFW_OS_BASED_VALUE(79, 0x67, 90, DOM_VK_NUMPAD7)] = RGFW_KP_7, - [RGFW_OS_BASED_VALUE(80, 0x68, 92, DOM_VK_NUMPAD8)] = RGFW_KP_8, - [RGFW_OS_BASED_VALUE(81, 0x69, 93, DOM_VK_NUMPAD9)] = RGFW_KP_9, - [RGFW_OS_BASED_VALUE(90, 0x60, 83, DOM_VK_NUMPAD0)] = RGFW_KP_0, - [RGFW_OS_BASED_VALUE(91, 0x6E, 65, DOM_VK_DECIMAL)] = RGFW_KP_Period, - [RGFW_OS_BASED_VALUE(104, 0x92, 77, 0)] = RGFW_KP_Return, + RGFW_MAP [RGFW_OS_BASED_VALUE(36, 0x0D, 36, DOM_VK_RETURN, KEY_ENTER)] = RGFW_Return RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(119, 0x2E, 118, DOM_VK_DELETE, KEY_DELETE)] = RGFW_Delete RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(77, 0x90, 72, DOM_VK_NUM_LOCK, KEY_NUMLOCK)] = RGFW_Numlock RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(106, 0x6F, 82, DOM_VK_DIVIDE, KEY_KPSLASH)] = RGFW_KP_Slash RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(63, 0x6A, 76, DOM_VK_MULTIPLY, KEY_KPASTERISK)] = RGFW_Multiply RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(82, 0x6D, 67, DOM_VK_SUBTRACT, KEY_KPMINUS)] = RGFW_KP_Minus RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(87, 0x61, 84, DOM_VK_NUMPAD1, KEY_KP1)] = RGFW_KP_1 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(88, 0x62, 85, DOM_VK_NUMPAD2, KEY_KP2)] = RGFW_KP_2 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(89, 0x63, 86, DOM_VK_NUMPAD3, KEY_KP3)] = RGFW_KP_3 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(83, 0x64, 87, DOM_VK_NUMPAD4, KEY_KP4)] = RGFW_KP_4 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(84, 0x65, 88, DOM_VK_NUMPAD5, KEY_KP5)] = RGFW_KP_5 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(85, 0x66, 89, DOM_VK_NUMPAD6, KEY_KP6)] = RGFW_KP_6 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(79, 0x67, 90, DOM_VK_NUMPAD7, KEY_KP7)] = RGFW_KP_7 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(80, 0x68, 92, DOM_VK_NUMPAD8, KEY_KP8)] = RGFW_KP_8 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(81, 0x69, 93, DOM_VK_NUMPAD9, KEY_KP9)] = RGFW_KP_9 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(90, 0x60, 83, DOM_VK_NUMPAD0, KEY_KP0)] = RGFW_KP_0 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(91, 0x6E, 65, DOM_VK_DECIMAL, KEY_KPDOT)] = RGFW_KP_Period RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(104, 0x92, 77, 0, KEY_KPENTER)] = RGFW_KP_Return, - [RGFW_OS_BASED_VALUE(20, 189, 27, DOM_VK_HYPHEN_MINUS)] = RGFW_Minus, - [RGFW_OS_BASED_VALUE(21, 187, 24, DOM_VK_EQUALS)] = RGFW_Equals, - [RGFW_OS_BASED_VALUE(22, 8, 51, DOM_VK_BACK_SPACE)] = RGFW_BackSpace, - [RGFW_OS_BASED_VALUE(23, 0x09, 48, DOM_VK_TAB)] = RGFW_Tab, - [RGFW_OS_BASED_VALUE(66, 20, 57, DOM_VK_CAPS_LOCK)] = RGFW_CapsLock, - [RGFW_OS_BASED_VALUE(50, 0xA0, 56, DOM_VK_SHIFT)] = RGFW_ShiftL, - [RGFW_OS_BASED_VALUE(37, 0x11, 59, DOM_VK_CONTROL)] = RGFW_ControlL, - [RGFW_OS_BASED_VALUE(64, 164, 58, DOM_VK_ALT)] = RGFW_AltL, - [RGFW_OS_BASED_VALUE(133, 0x5B, 55, DOM_VK_WIN)] = RGFW_SuperL, + RGFW_MAP [RGFW_OS_BASED_VALUE(20, 189, 27, DOM_VK_HYPHEN_MINUS, KEY_MINUS)] = RGFW_Minus RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(21, 187, 24, DOM_VK_EQUALS, KEY_EQUAL)] = RGFW_Equals RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(22, 8, 51, DOM_VK_BACK_SPACE, KEY_BACKSPACE)] = RGFW_BackSpace RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(23, 0x09, 48, DOM_VK_TAB, KEY_TAB)] = RGFW_Tab RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(66, 20, 57, DOM_VK_CAPS_LOCK, KEY_CAPSLOCK)] = RGFW_CapsLock RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(50, 0x10, 56, DOM_VK_SHIFT, KEY_LEFTSHIFT)] = RGFW_ShiftL RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(37, 0x11, 59, DOM_VK_CONTROL, KEY_LEFTCTRL)] = RGFW_ControlL RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(64,0x12, 58, DOM_VK_ALT, KEY_LEFTALT)] = RGFW_AltL RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(133, 0x5B, 55, DOM_VK_WIN, KEY_LEFTMETA)] = RGFW_SuperL, #if !defined(RGFW_WINDOWS) && !defined(RGFW_MACOS) && !defined(RGFW_WEBASM) - [RGFW_OS_BASED_VALUE(105, 0x11, 59, 0)] = RGFW_ControlR, - [RGFW_OS_BASED_VALUE(135, 0xA4, 55, 0)] = RGFW_SuperR, + RGFW_MAP [RGFW_OS_BASED_VALUE(105, 0x11, 59, 0, KEY_RIGHTCTRL)] = RGFW_ControlR RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(135, 0xA4, 55, 0, KEY_RIGHTMETA)] = RGFW_SuperR, + RGFW_MAP [RGFW_OS_BASED_VALUE(62, 0x5C, 56, 0, KEY_RIGHTSHIFT)] = RGFW_ShiftR RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(108, 165, 58, 0, KEY_RIGHTALT)] = RGFW_AltR, #endif - #if !defined(RGFW_MACOS) && !defined(RGFW_WEBASM) - [RGFW_OS_BASED_VALUE(62, 0x5C, 56, 0)] = RGFW_ShiftR, - [RGFW_OS_BASED_VALUE(108, 165, 58, 0)] = RGFW_AltR, - #endif - - [RGFW_OS_BASED_VALUE(67, 0x70, 127, DOM_VK_F1)] = RGFW_F1, - [RGFW_OS_BASED_VALUE(68, 0x71, 121, DOM_VK_F2)] = RGFW_F2, - [RGFW_OS_BASED_VALUE(69, 0x72, 100, DOM_VK_F3)] = RGFW_F3, - [RGFW_OS_BASED_VALUE(70, 0x73, 119, DOM_VK_F4)] = RGFW_F4, - [RGFW_OS_BASED_VALUE(71, 0x74, 97, DOM_VK_F5)] = RGFW_F5, - [RGFW_OS_BASED_VALUE(72, 0x75, 98, DOM_VK_F6)] = RGFW_F6, - [RGFW_OS_BASED_VALUE(73, 0x76, 99, DOM_VK_F7)] = RGFW_F7, - [RGFW_OS_BASED_VALUE(74, 0x77, 101, DOM_VK_F8)] = RGFW_F8, - [RGFW_OS_BASED_VALUE(75, 0x78, 102, DOM_VK_F9)] = RGFW_F9, - [RGFW_OS_BASED_VALUE(76, 0x79, 110, DOM_VK_F10)] = RGFW_F10, - [RGFW_OS_BASED_VALUE(95, 0x7A, 104, DOM_VK_F11)] = RGFW_F11, - [RGFW_OS_BASED_VALUE(96, 0x7B, 112, DOM_VK_F12)] = RGFW_F12, - [RGFW_OS_BASED_VALUE(111, 0x26, 126, DOM_VK_UP)] = RGFW_Up, - [RGFW_OS_BASED_VALUE(116, 0x28, 125, DOM_VK_DOWN)] = RGFW_Down, - [RGFW_OS_BASED_VALUE(113, 0x25, 123, DOM_VK_LEFT)] = RGFW_Left, - [RGFW_OS_BASED_VALUE(114, 0x27, 124, DOM_VK_RIGHT)] = RGFW_Right, - [RGFW_OS_BASED_VALUE(118, 0x2D, 115, DOM_VK_INSERT)] = RGFW_Insert, - [RGFW_OS_BASED_VALUE(115, 0x23, 120, DOM_VK_END)] = RGFW_End, - [RGFW_OS_BASED_VALUE(112, 336, 117, DOM_VK_PAGE_UP)] = RGFW_PageUp, - [RGFW_OS_BASED_VALUE(117, 325, 122, DOM_VK_PAGE_DOWN)] = RGFW_PageDown, - [RGFW_OS_BASED_VALUE(9, 0x1B, 53, DOM_VK_ESCAPE)] = RGFW_Escape, - [RGFW_OS_BASED_VALUE(110, 0x24, 116, DOM_VK_HOME)] = RGFW_Home, + RGFW_MAP [RGFW_OS_BASED_VALUE(67, 0x70, 127, DOM_VK_F1, KEY_F1)] = RGFW_F1 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(68, 0x71, 121, DOM_VK_F2, KEY_F2)] = RGFW_F2 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(69, 0x72, 100, DOM_VK_F3, KEY_F3)] = RGFW_F3 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(70, 0x73, 119, DOM_VK_F4, KEY_F4)] = RGFW_F4 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(71, 0x74, 97, DOM_VK_F5, KEY_F5)] = RGFW_F5 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(72, 0x75, 98, DOM_VK_F6, KEY_F6)] = RGFW_F6 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(73, 0x76, 99, DOM_VK_F7, KEY_F7)] = RGFW_F7 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(74, 0x77, 101, DOM_VK_F8, KEY_F8)] = RGFW_F8 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(75, 0x78, 102, DOM_VK_F9, KEY_F9)] = RGFW_F9 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(76, 0x79, 110, DOM_VK_F10, KEY_F10)] = RGFW_F10 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(95, 0x7A, 104, DOM_VK_F11, KEY_F11)] = RGFW_F11 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(96, 0x7B, 112, DOM_VK_F12, KEY_F12)] = RGFW_F12 RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(111, 0x26, 126, DOM_VK_UP, KEY_UP)] = RGFW_Up RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(116, 0x28, 125, DOM_VK_DOWN, KEY_DOWN)] = RGFW_Down RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(113, 0x25, 123, DOM_VK_LEFT, KEY_LEFT)] = RGFW_Left RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(114, 0x27, 124, DOM_VK_RIGHT, KEY_RIGHT)] = RGFW_Right RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(118, 0x2D, 115, DOM_VK_INSERT, KEY_INSERT)] = RGFW_Insert RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(115, 0x23, 120, DOM_VK_END, KEY_END)] = RGFW_End RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(112, 336, 117, DOM_VK_PAGE_UP, KEY_PAGEUP)] = RGFW_PageUp RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(117, 325, 122, DOM_VK_PAGE_DOWN, KEY_PAGEDOWN)] = RGFW_PageDown RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(9, 0x1B, 53, DOM_VK_ESCAPE, KEY_ESC)] = RGFW_Escape RGFW_NEXT + RGFW_MAP [RGFW_OS_BASED_VALUE(110, 0x24, 116, DOM_VK_HOME, KEY_HOME)] = RGFW_Home RGFW_NEXT +#ifndef __cplusplus }; +#else +} +#endif + +#undef RGFW_NEXT +#undef RGFW_MAP typedef struct { b8 current : 1; @@ -1244,6 +1368,12 @@ RGFW_keyState RGFW_keyboard[final_key] = { {0, 0} }; RGFWDEF u32 RGFW_apiKeyCodeToRGFW(u32 keycode); u32 RGFW_apiKeyCodeToRGFW(u32 keycode) { + #ifdef __cplusplus + if (RGFW_OS_BASED_VALUE(49, 192, 50, DOM_VK_BACK_QUOTE, KEY_GRAVE) != RGFW_Backtick) { + RGFW_init_keys(); + } + #endif + /* make sure the key isn't out of bounds */ if (keycode > sizeof(RGFW_keycodes) / sizeof(u8)) return 0; @@ -1253,22 +1383,54 @@ u32 RGFW_apiKeyCodeToRGFW(u32 keycode) { RGFWDEF void RGFW_resetKey(void); void RGFW_resetKey(void) { - size_t len = final_key; /* last_key == length */ + size_t len = final_key; /*!< last_key == length */ - size_t i; /* reset each previous state */ + size_t i; /*!< reset each previous state */ for (i = 0; i < len; i++) RGFW_keyboard[i].prev = 0; } +b8 RGFW_shouldShift(u32 keycode, u8 lockState) { + #define RGFW_xor(x, y) (( (x) && (!(y)) ) || ((y) && (!(x)) )) + b8 caps4caps = (lockState & RGFW_CAPSLOCK) && ((keycode >= RGFW_a) && (keycode <= RGFW_z)); + b8 shouldShift = RGFW_xor((RGFW_isPressed(NULL, RGFW_ShiftL) || RGFW_isPressed(NULL, RGFW_ShiftR)), caps4caps); + #undef RGFW_xor + + return shouldShift; +} + +char RGFW_keyCodeToChar(u32 keycode, b8 shift) { + static const char map[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '`', '0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', '-', '=', 0, '\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '.', ',', '/', '[', ']', ';', '\n', '\'', '\\', + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '/', '*', '-', '1', '2', '3', '3', '5', '6', '7', '8', '9', '0', '\n' + }; + + static const char mapCaps[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '~', ')', '!', '@', '#', '$', '%', '^', '&', '*', + '(', '_', '+', 0, '0', 0, 0, 0, 0, 0, 0, 0, 0, 0, ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '>', '<', '?', '{', '}', ':', '\n', '"', '|', + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '?', '*', '-', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + if (shift == RGFW_FALSE) + return map[keycode]; + return mapCaps[keycode]; +} + +char RGFW_keyCodeToCharAuto(u32 keycode, u8 lockState) { return RGFW_keyCodeToChar(keycode, RGFW_shouldShift(keycode, lockState)); } + /* this is the end of keycode data */ /* joystick data */ -u8 RGFW_jsPressed[4][16]; /* if a key is currently pressed or not (per joystick) */ +u8 RGFW_jsPressed[4][16]; /*!< if a key is currently pressed or not (per joystick) */ -i32 RGFW_joysticks[4]; /* limit of 4 joysticks at a time */ -u16 RGFW_joystickCount; /* the actual amount of joysticks */ +i32 RGFW_joysticks[4]; /*!< limit of 4 joysticks at a time */ +u16 RGFW_joystickCount; /*!< the actual amount of joysticks */ /* event callback defines start here @@ -1325,19 +1487,73 @@ void RGFW_window_checkEvents(RGFW_window* win, i32 waitMS) { #endif } -void RGFW_setWindowMoveCallback(RGFW_windowmovefunc func) { RGFW_windowMoveCallback = func; } -void RGFW_setWindowResizeCallback(RGFW_windowresizefunc func) { RGFW_windowResizeCallback = func; } -void RGFW_setWindowQuitCallback(RGFW_windowquitfunc func) { RGFW_windowQuitCallback = func; } -void RGFW_setMousePosCallback(RGFW_mouseposfunc func) { RGFW_mousePosCallback = func; } -void RGFW_setWindowRefreshCallback(RGFW_windowrefreshfunc func) { RGFW_windowRefreshCallback = func; } -void RGFW_setFocusCallback(RGFW_focusfunc func) { RGFW_focusCallback = func; } -void RGFW_setMouseNotifyCallBack(RGFW_mouseNotifyfunc func) { RGFW_mouseNotifyCallBack = func; } -void RGFW_setDndCallback(RGFW_dndfunc func) { RGFW_dndCallback = func; } -void RGFW_setDndInitCallback(RGFW_dndInitfunc func) { RGFW_dndInitCallback = func; } -void RGFW_setKeyCallback(RGFW_keyfunc func) { RGFW_keyCallback = func; } -void RGFW_setMouseButtonCallback(RGFW_mousebuttonfunc func) { RGFW_mouseButtonCallback = func; } -void RGFW_setjsButtonCallback(RGFW_jsButtonfunc func) { RGFW_jsButtonCallback = func; } -void RGFW_setjsAxisCallback(RGFW_jsAxisfunc func) { RGFW_jsAxisCallback = func; } +RGFW_windowmovefunc RGFW_setWindowMoveCallback(RGFW_windowmovefunc func) { + RGFW_windowmovefunc prev = (RGFW_windowMoveCallback == RGFW_windowmovefuncEMPTY) ? NULL : RGFW_windowMoveCallback; + RGFW_windowMoveCallback = func; + return prev; +} +RGFW_windowresizefunc RGFW_setWindowResizeCallback(RGFW_windowresizefunc func) { + RGFW_windowresizefunc prev = (RGFW_windowResizeCallback == RGFW_windowresizefuncEMPTY) ? NULL : RGFW_windowResizeCallback; + RGFW_windowResizeCallback = func; + return prev; +} +RGFW_windowquitfunc RGFW_setWindowQuitCallback(RGFW_windowquitfunc func) { + RGFW_windowquitfunc prev = (RGFW_windowQuitCallback == RGFW_windowquitfuncEMPTY) ? NULL : RGFW_windowQuitCallback; + RGFW_windowQuitCallback = func; + return prev; +} + +RGFW_mouseposfunc RGFW_setMousePosCallback(RGFW_mouseposfunc func) { + RGFW_mouseposfunc prev = (RGFW_mousePosCallback == RGFW_mouseposfuncEMPTY) ? NULL : RGFW_mousePosCallback; + RGFW_mousePosCallback = func; + return prev; +} +RGFW_windowrefreshfunc RGFW_setWindowRefreshCallback(RGFW_windowrefreshfunc func) { + RGFW_windowrefreshfunc prev = (RGFW_windowRefreshCallback == RGFW_windowrefreshfuncEMPTY) ? NULL : RGFW_windowRefreshCallback; + RGFW_windowRefreshCallback = func; + return prev; +} +RGFW_focusfunc RGFW_setFocusCallback(RGFW_focusfunc func) { + RGFW_focusfunc prev = (RGFW_focusCallback == RGFW_focusfuncEMPTY) ? NULL : RGFW_focusCallback; + RGFW_focusCallback = func; + return prev; +} + +RGFW_mouseNotifyfunc RGFW_setMouseNotifyCallBack(RGFW_mouseNotifyfunc func) { + RGFW_mouseNotifyfunc prev = (RGFW_mouseNotifyCallBack == RGFW_mouseNotifyfuncEMPTY) ? NULL : RGFW_mouseNotifyCallBack; + RGFW_mouseNotifyCallBack = func; + return prev; +} +RGFW_dndfunc RGFW_setDndCallback(RGFW_dndfunc func) { + RGFW_dndfunc prev = (RGFW_dndCallback == RGFW_dndfuncEMPTY) ? NULL : RGFW_dndCallback; + RGFW_dndCallback = func; + return prev; +} +RGFW_dndInitfunc RGFW_setDndInitCallback(RGFW_dndInitfunc func) { + RGFW_dndInitfunc prev = (RGFW_dndInitCallback == RGFW_dndInitfuncEMPTY) ? NULL : RGFW_dndInitCallback; + RGFW_dndInitCallback = func; + return prev; +} +RGFW_keyfunc RGFW_setKeyCallback(RGFW_keyfunc func) { + RGFW_keyfunc prev = (RGFW_keyCallback == RGFW_keyfuncEMPTY) ? NULL : RGFW_keyCallback; + RGFW_keyCallback = func; + return prev; +} +RGFW_mousebuttonfunc RGFW_setMouseButtonCallback(RGFW_mousebuttonfunc func) { + RGFW_mousebuttonfunc prev = (RGFW_mouseButtonCallback == RGFW_mousebuttonfuncEMPTY) ? NULL : RGFW_mouseButtonCallback; + RGFW_mouseButtonCallback = func; + return prev; +} +RGFW_jsButtonfunc RGFW_setjsButtonCallback(RGFW_jsButtonfunc func) { + RGFW_jsButtonfunc prev = (RGFW_jsButtonCallback == RGFW_jsButtonfuncEMPTY) ? NULL : RGFW_jsButtonCallback; + RGFW_jsButtonCallback = func; + return prev; +} +RGFW_jsAxisfunc RGFW_setjsAxisCallback(RGFW_jsAxisfunc func) { + RGFW_jsAxisfunc prev = (RGFW_jsAxisCallback == RGFW_jsAxisfuncEMPTY) ? NULL : RGFW_jsAxisCallback; + RGFW_jsAxisCallback = func; + return prev; +} /* no more event call back defines */ @@ -1368,7 +1584,7 @@ RGFWDEF RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args); /* do a basic initialization for RGFW_window, this is to standard it for each OS */ RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args) { - RGFW_window* win = (RGFW_window*) RGFW_MALLOC(sizeof(RGFW_window)); /* make a new RGFW struct */ + RGFW_window* win = (RGFW_window*) RGFW_MALLOC(sizeof(RGFW_window)); /*!< make a new RGFW struct */ /* clear out dnd info */ #ifdef RGFW_ALLOC_DROPFILES @@ -1386,7 +1602,7 @@ RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args) { assert(win->src.display != NULL); Screen* scrn = DefaultScreenOfDisplay((Display*)win->src.display); - RGFW_area screenR = RGFW_AREA(scrn->width, scrn->height); + RGFW_area screenR = RGFW_AREA((u32)scrn->width, (u32)scrn->height); #endif /* rect based the requested args */ @@ -1398,7 +1614,6 @@ RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args) { /* set and init the new window's data */ win->r = rect; - win->fpsCap = 0; win->event.inFocus = 1; win->event.droppedFilesCount = 0; RGFW_joystickCount = 0; @@ -1424,7 +1639,7 @@ RGFW_window* RGFW_root = NULL; void RGFW_clipboardFree(char* str) { RGFW_FREE(str); } -RGFW_keyState RGFW_mouseButtons[5] = { 0 }; +RGFW_keyState RGFW_mouseButtons[5] = { {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} }; b8 RGFW_isMousePressed(RGFW_window* win, u8 button) { assert(win != NULL); @@ -1461,20 +1676,28 @@ b8 RGFW_isReleased(RGFW_window* win, u8 key) { return (!RGFW_isPressed(win, key) && RGFW_wasPressed(win, key)); } -void RGFW_window_makeCurrent(RGFW_window* win) { - assert(win != NULL); +#if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX) /* defines for directX context*/ + RGFW_directXinfo RGFW_dxInfo; + RGFW_directXinfo* RGFW_getDirectXInfo(void) { return &RGFW_dxInfo; } +#endif +void RGFW_window_makeCurrent(RGFW_window* win) { #if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX) - RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, &win->src.renderTargetView, NULL); + if (win == NULL) + RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, NULL, NULL); + else + RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, &win->src.renderTargetView, NULL); #elif defined(RGFW_OPENGL) RGFW_window_makeCurrent_OpenGL(win); +#else + RGFW_UNUSED(win) #endif } void RGFW_window_setGPURender(RGFW_window* win, i8 set) { if (!set && !(win->_winArgs & RGFW_NO_GPU_RENDER)) win->_winArgs |= RGFW_NO_GPU_RENDER; - + else if (set && win->_winArgs & RGFW_NO_GPU_RENDER) win->_winArgs ^= RGFW_NO_GPU_RENDER; } @@ -1510,6 +1733,7 @@ void RGFW_window_setShouldClose(RGFW_window* win) { win->event.type = RGFW_quit; #endif RGFWDEF void RGFW_captureCursor(RGFW_window* win, RGFW_rect); +RGFWDEF void RGFW_releaseCursor(RGFW_window* win); void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area) { if ((win->_winArgs & RGFW_HOLD_MOUSE)) @@ -1521,25 +1745,26 @@ void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area) { win->_winArgs |= RGFW_HOLD_MOUSE; RGFW_captureCursor(win, win->r); - RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (area.w), win->r.y + (area.h))); + RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2))); } void RGFW_window_mouseUnhold(RGFW_window* win) { if ((win->_winArgs & RGFW_HOLD_MOUSE)) { win->_winArgs ^= RGFW_HOLD_MOUSE; - RGFW_captureCursor(win, RGFW_RECT(0, 0, 0, 0)); + RGFW_releaseCursor(win); } } -void RGFW_window_checkFPS(RGFW_window* win) { +u32 RGFW_window_checkFPS(RGFW_window* win, u32 fpsCap) { u64 deltaTime = RGFW_getTimeNS() - win->event.frameTime; + u32 output_fps = 0; u64 fps = round(1e+9 / deltaTime); - win->event.fps = fps; + output_fps= fps; - if (win->fpsCap && fps > win->fpsCap) { - u64 frameTimeNS = 1e+9 / win->fpsCap; + if (fpsCap && fps > fpsCap) { + u64 frameTimeNS = 1e+9 / fpsCap; u64 sleepTimeMS = (frameTimeNS - deltaTime) / 1e6; if (sleepTimeMS > 0) { @@ -1550,12 +1775,14 @@ void RGFW_window_checkFPS(RGFW_window* win) { win->event.frameTime = RGFW_getTimeNS(); - if (win->fpsCap == 0) - return; + if (fpsCap == 0) + return (u32) output_fps; deltaTime = RGFW_getTimeNS() - win->event.frameTime2; - win->event.fps = round(1e+9 / deltaTime); + output_fps = round(1e+9 / deltaTime); win->event.frameTime2 = RGFW_getTimeNS(); + + return output_fps; } u32 RGFW_isPressedJS(RGFW_window* win, u16 c, u8 button) { @@ -1586,7 +1813,7 @@ void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { win->event.lockState ^= RGFW_NUMLOCK; } -#if defined(RGFW_X11) || defined(RGFW_MACOS) +#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WEBASM) || defined(RGFW_WAYLAND) struct timespec; int nanosleep(const struct timespec* duration, struct timespec* rem); @@ -1603,7 +1830,7 @@ void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { #endif /* - graphics API spcific code (end of generic code) + graphics API specific code (end of generic code) starts here */ @@ -1615,12 +1842,13 @@ void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { #if defined(RGFW_OPENGL) || defined(RGFW_EGL) || defined(RGFW_OSMESA) #ifdef RGFW_WINDOWS #define WIN32_LEAN_AND_MEAN + #define OEMRESOURCE #include #endif - #ifndef __APPLE__ + #if !defined(__APPLE__) && !defined(RGFW_NO_GL_HEADER) #include - #else + #elif defined(__APPLE__) #ifndef GL_SILENCE_DEPRECATION #define GL_SILENCE_DEPRECATION #endif @@ -1631,11 +1859,12 @@ void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { /* EGL, normal OpenGL only */ #if !defined(RGFW_OSMESA) i32 RGFW_majorVersion = 0, RGFW_minorVersion = 0; + b8 RGFW_profile = RGFW_GL_CORE; #ifndef RGFW_EGL - i32 RGFW_STENCIL = 8, RGFW_SAMPLES = 4, RGFW_STEREO = GL_FALSE, RGFW_AUX_BUFFERS = 0; + i32 RGFW_STENCIL = 8, RGFW_SAMPLES = 4, RGFW_STEREO = 0, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1; #else - i32 RGFW_STENCIL = 0, RGFW_SAMPLES = 0, RGFW_STEREO = GL_FALSE, RGFW_AUX_BUFFERS = 0; + i32 RGFW_STENCIL = 0, RGFW_SAMPLES = 0, RGFW_STEREO = 0, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1; #endif @@ -1643,55 +1872,44 @@ void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { void RGFW_setGLSamples(i32 samples) { RGFW_SAMPLES = samples; } void RGFW_setGLStereo(i32 stereo) { RGFW_STEREO = stereo; } void RGFW_setGLAuxBuffers(i32 auxBuffers) { RGFW_AUX_BUFFERS = auxBuffers; } + void RGFW_setDoubleBuffer(b8 useDoubleBuffer) { RGFW_DOUBLE_BUFFER = useDoubleBuffer; } - void RGFW_setGLVersion(i32 major, i32 minor) { + void RGFW_setGLVersion(b8 profile, i32 major, i32 minor) { + RGFW_profile = profile; RGFW_majorVersion = major; RGFW_minorVersion = minor; } - u8* RGFW_getMaxGLVersion(void) { - RGFW_window* dummy = RGFW_createWindow("dummy", RGFW_RECT(0, 0, 1, 1), 0); - - const char* versionStr = (const char*) glGetString(GL_VERSION); - - static u8 version[2]; - version[0] = versionStr[0] - '0', - version[1] = versionStr[2] - '0'; - - RGFW_window_close(dummy); - - return version; - } - /* OPENGL normal only (no EGL / OSMesa) */ #ifndef RGFW_EGL -#define RGFW_GL_RENDER_TYPE RGFW_OS_BASED_VALUE(GLX_X_VISUAL_TYPE, 0x2003, 73, 0) - #define RGFW_GL_ALPHA_SIZE RGFW_OS_BASED_VALUE(GLX_ALPHA_SIZE, 0x201b, 11, 0) - #define RGFW_GL_DEPTH_SIZE RGFW_OS_BASED_VALUE(GLX_DEPTH_SIZE, 0x2022, 12, 0) - #define RGFW_GL_DOUBLEBUFFER RGFW_OS_BASED_VALUE(GLX_DOUBLEBUFFER, 0x2011, 5, 0) - #define RGFW_GL_STENCIL_SIZE RGFW_OS_BASED_VALUE(GLX_STENCIL_SIZE, 0x2023, 13, 0) - #define RGFW_GL_SAMPLES RGFW_OS_BASED_VALUE(GLX_SAMPLES, 0x2042, 55, 0) - #define RGFW_GL_STEREO RGFW_OS_BASED_VALUE(GLX_STEREO, 0x2012, 6, 0) - #define RGFW_GL_AUX_BUFFERS RGFW_OS_BASED_VALUE(GLX_AUX_BUFFERS, 0x2024, 7, 0) +#define RGFW_GL_RENDER_TYPE RGFW_OS_BASED_VALUE(GLX_X_VISUAL_TYPE, 0x2003, 73, 0, 0) + #define RGFW_GL_ALPHA_SIZE RGFW_OS_BASED_VALUE(GLX_ALPHA_SIZE, 0x201b, 11, 0, 0) + #define RGFW_GL_DEPTH_SIZE RGFW_OS_BASED_VALUE(GLX_DEPTH_SIZE, 0x2022, 12, 0, 0) + #define RGFW_GL_DOUBLEBUFFER RGFW_OS_BASED_VALUE(GLX_DOUBLEBUFFER, 0x2011, 5, 0, 0) + #define RGFW_GL_STENCIL_SIZE RGFW_OS_BASED_VALUE(GLX_STENCIL_SIZE, 0x2023, 13, 0, 0) + #define RGFW_GL_SAMPLES RGFW_OS_BASED_VALUE(GLX_SAMPLES, 0x2042, 55, 0, 0) + #define RGFW_GL_STEREO RGFW_OS_BASED_VALUE(GLX_STEREO, 0x2012, 6, 0, 0) + #define RGFW_GL_AUX_BUFFERS RGFW_OS_BASED_VALUE(GLX_AUX_BUFFERS, 0x2024, 7, 0, 0) #if defined(RGFW_X11) || defined(RGFW_WINDOWS) - #define RGFW_GL_DRAW RGFW_OS_BASED_VALUE(GLX_X_RENDERABLE, 0x2001, 0, 0) - #define RGFW_GL_DRAW_TYPE RGFW_OS_BASED_VALUE(GLX_RENDER_TYPE, 0x2013, 0, 0) - #define RGFW_GL_USE_OPENGL RGFW_OS_BASED_VALUE(GLX_USE_GL, 0x2010, 0, 0) - #define RGFW_GL_FULL_FORMAT RGFW_OS_BASED_VALUE(GLX_TRUE_COLOR, 0x2027, 0, 0) - #define RGFW_GL_RED_SIZE RGFW_OS_BASED_VALUE(GLX_RED_SIZE, 0x2015, 0, 0) - #define RGFW_GL_GREEN_SIZE RGFW_OS_BASED_VALUE(GLX_GREEN_SIZE, 0x2017, 0, 0) - #define RGFW_GL_BLUE_SIZE RGFW_OS_BASED_VALUE(GLX_BLUE_SIZE, 0x2019, 0, 0) - #define RGFW_GL_USE_RGBA RGFW_OS_BASED_VALUE(GLX_RGBA_BIT, 0x202B, 0, 0) + #define RGFW_GL_DRAW RGFW_OS_BASED_VALUE(GLX_X_RENDERABLE, 0x2001, 0, 0, 0) + #define RGFW_GL_DRAW_TYPE RGFW_OS_BASED_VALUE(GLX_RENDER_TYPE, 0x2013, 0, 0, 0) + #define RGFW_GL_FULL_FORMAT RGFW_OS_BASED_VALUE(GLX_TRUE_COLOR, 0x2027, 0, 0, 0) + #define RGFW_GL_RED_SIZE RGFW_OS_BASED_VALUE(GLX_RED_SIZE, 0x2015, 0, 0, 0) + #define RGFW_GL_GREEN_SIZE RGFW_OS_BASED_VALUE(GLX_GREEN_SIZE, 0x2017, 0, 0, 0) + #define RGFW_GL_BLUE_SIZE RGFW_OS_BASED_VALUE(GLX_BLUE_SIZE, 0x2019, 0, 0, 0) + #define RGFW_GL_USE_RGBA RGFW_OS_BASED_VALUE(GLX_RGBA_BIT, 0x202B, 0, 0, 0) #endif #ifdef RGFW_WINDOWS + #define WGL_SUPPORT_OPENGL_ARB 0x2010 #define WGL_COLOR_BITS_ARB 0x2014 #define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 #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_SAMPLE_BUFFERS_ARB 0x2041 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 @@ -1700,8 +1918,12 @@ void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { #define WGL_TRANSPARENT_ARB 0x200A #endif - - static u32* RGFW_initAttribs(u32 useSoftware) { + +/* The window'ing api needs to know how to render the data we (or opengl) give it + MacOS and Windows do this using a structure called a "pixel format" + X11 calls it a "Visual" + This function returns the attributes for the format we want */ + static u32* RGFW_initFormatAttribs(u32 useSoftware) { RGFW_UNUSED(useSoftware); static u32 attribs[] = { #if defined(RGFW_X11) || defined(RGFW_WINDOWS) @@ -1710,13 +1932,7 @@ void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { #endif RGFW_GL_ALPHA_SIZE , 8, RGFW_GL_DEPTH_SIZE , 24, - RGFW_GL_DOUBLEBUFFER , - #ifndef RGFW_MACOS - 1, - #endif - #if defined(RGFW_X11) || defined(RGFW_WINDOWS) - RGFW_GL_USE_OPENGL, 1, RGFW_GL_DRAW, 1, RGFW_GL_RED_SIZE , 8, RGFW_GL_GREEN_SIZE , 8, @@ -1726,7 +1942,7 @@ void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { #ifdef RGFW_X11 GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, - #endif + #endif #ifdef RGFW_MACOS 72, @@ -1734,8 +1950,8 @@ void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { #endif #ifdef RGFW_WINDOWS + WGL_SUPPORT_OPENGL_ARB, 1, WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, - WGL_TRANSPARENT_ARB, TRUE, WGL_COLOR_BITS_ARB, 32, #endif @@ -1750,8 +1966,10 @@ void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { attribs[index + 1] = attVal;\ index += 2;\ } - - RGFW_GL_ADD_ATTRIB(RGFW_GL_STENCIL_SIZE, RGFW_STENCIL); + + RGFW_GL_ADD_ATTRIB(RGFW_GL_DOUBLEBUFFER, 1); + + RGFW_GL_ADD_ATTRIB(RGFW_GL_STENCIL_SIZE, RGFW_STENCIL); RGFW_GL_ADD_ATTRIB(RGFW_GL_STEREO, RGFW_STEREO); RGFW_GL_ADD_ATTRIB(RGFW_GL_AUX_BUFFERS, RGFW_AUX_BUFFERS); @@ -1769,13 +1987,15 @@ void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { #endif #ifdef RGFW_MACOS + /* macOS has the surface attribs and the opengl attribs connected for some reason + maybe this is to give macOS more control to limit openGL/the opengl version? */ + attribs[index] = 99; attribs[index + 1] = 0x1000; if (RGFW_majorVersion >= 4 || RGFW_majorVersion >= 3) { attribs[index + 1] = (u32) ((RGFW_majorVersion >= 4) ? 0x4100 : 0x3200); } - #endif RGFW_GL_ADD_ATTRIB(0, 0); @@ -1894,18 +2114,29 @@ void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { #else 2, #endif - EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE + EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE }; size_t index = 4; RGFW_GL_ADD_ATTRIB(EGL_STENCIL_SIZE, RGFW_STENCIL); RGFW_GL_ADD_ATTRIB(EGL_SAMPLES, RGFW_SAMPLES); + if (RGFW_DOUBLE_BUFFER) + RGFW_GL_ADD_ATTRIB(EGL_RENDER_BUFFER, EGL_BACK_BUFFER); + if (RGFW_majorVersion) { attribs[1] = RGFW_majorVersion; - RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT); + RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MAJOR_VERSION, RGFW_majorVersion); RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MINOR_VERSION, RGFW_minorVersion); + + if (RGFW_profile == RGFW_GL_CORE) { + RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT); + } + else { + RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT); + } + } #if defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3) @@ -1913,13 +2144,20 @@ void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { #else eglBindAPI(EGL_OPENGL_API); #endif - - win->src.EGL_context = eglCreateContext(win->src.EGL_display, config, EGL_NO_CONTEXT, attribs); + + win->src.EGL_context = eglCreateContext(win->src.EGL_display, config, EGL_NO_CONTEXT, attribs); + + if (win->src.EGL_context == NULL) + fprintf(stderr, "failed to create an EGL opengl context\n"); eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.EGL_context); eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); } + void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { + eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.EGL_context); + } + #ifdef RGFW_APPLE void* RGFWnsglFramework = NULL; #elif defined(RGFW_WINDOWS) @@ -1949,8 +2187,6 @@ void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { eglSwapInterval(win->src.EGL_display, swapInterval); - win->fpsCap = (swapInterval == 1) ? 0 : swapInterval; - } #endif /* RGFW_EGL */ @@ -1990,6 +2226,62 @@ This is where OS specific stuff starts */ +#if defined(RGFW_WAYLAND) || defined(RGFW_X11) + int RGFW_eventWait_forceStop[] = {0, 0, 0}; /* for wait events */ + + #ifdef __linux__ + #include + #include + #include + + RGFW_Event* RGFW_linux_updateJoystick(RGFW_window* win) { + static int xAxis = 0, yAxis = 0; + u8 i; + for (i = 0; i < RGFW_joystickCount; i++) { + struct js_event e; + + + if (RGFW_joysticks[i] == 0) + continue; + + i32 flags = fcntl(RGFW_joysticks[i], F_GETFL, 0); + fcntl(RGFW_joysticks[i], F_SETFL, flags | O_NONBLOCK); + + ssize_t bytes; + while ((bytes = read(RGFW_joysticks[i], &e, sizeof(e))) > 0) { + switch (e.type) { + case JS_EVENT_BUTTON: + win->event.type = e.value ? RGFW_jsButtonPressed : RGFW_jsButtonReleased; + win->event.button = e.number; + RGFW_jsPressed[i][e.number] = e.value; + RGFW_jsButtonCallback(win, i, e.number, e.value); + return &win->event; + case JS_EVENT_AXIS: + ioctl(RGFW_joysticks[i], JSIOCGAXES, &win->event.axisesCount); + + if ((e.number == 0 || e.number % 2) && e.number != 1) + xAxis = e.value; + else + yAxis = e.value; + + win->event.axis[e.number / 2].x = xAxis; + win->event.axis[e.number / 2].y = yAxis; + win->event.type = RGFW_jsAxisMove; + win->event.joystick = i; + RGFW_jsAxisCallback(win, i, win->event.axis, win->event.axisesCount); + return &win->event; + + default: break; + } + } + } + + return NULL; + } + + #endif +#endif + /* @@ -2021,9 +2313,9 @@ Start of Linux / Unix defines #include #include /* for data limits (mainly used in drag and drop functions) */ -#include #include + #ifdef __linux__ #include #endif @@ -2075,7 +2367,7 @@ Start of Linux / Unix defines if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0) RGFW_bufferSize = RGFW_getScreenSize(); - win->buffer = RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4); + win->buffer = (u8*)RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4); #ifdef RGFW_OSMESA win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL); @@ -2092,7 +2384,7 @@ Start of Linux / Unix defines win->src.gc = XCreateGC(win->src.display, win->src.window, 0, NULL); #else - RGFW_UNUSED(win); /* if buffer rendering is not being used */ + RGFW_UNUSED(win); /*!< if buffer rendering is not being used */ RGFW_UNUSED(vi) #endif } @@ -2117,30 +2409,30 @@ Start of Linux / Unix defines 32, PropModeReplace, (u8*)&hints, 5 ); } + + void RGFW_releaseCursor(RGFW_window* win) { + XUngrabPointer(win->src.display, CurrentTime); - void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { + /* disable raw input */ + unsigned char mask[] = { 0 }; XIEventMask em; em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; - /* grab the cursor if the rect struct isn't zeroed out, else ungrab*/ - if (!r.x && !r.y && r.w && !r.h) { - XUngrabPointer(win->src.display, CurrentTime); - - /* disable raw input */ - unsigned char mask[] = { 0 }; - em.mask_len = sizeof(mask); - em.mask = mask; - XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1); - return; - } - + XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1); + } + + void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { /* enable raw input */ unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; + XISetMask(mask, XI_RawMotion); + XIEventMask em; + em.deviceid = XIAllMasterDevices; em.mask_len = sizeof(mask); em.mask = mask; - XISetMask(mask, XI_RawMotion); - + XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1); XGrabPointer(win->src.display, win->src.window, True, PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); @@ -2179,24 +2471,24 @@ Start of Linux / Unix defines } #endif - XInitThreads(); /* init X11 threading*/ + XInitThreads(); /*!< init X11 threading*/ if (args & RGFW_OPENGL_SOFTWARE) setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1); RGFW_window* win = RGFW_window_basic_init(rect, args); - u64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | ExposureMask; /* X11 events accepted*/ + u64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | ExposureMask; /*!< X11 events accepted*/ #ifdef RGFW_OPENGL - u32* visual_attribs = RGFW_initAttribs(args & RGFW_OPENGL_SOFTWARE); + u32* visual_attribs = RGFW_initFormatAttribs(args & RGFW_OPENGL_SOFTWARE); i32 fbcount; GLXFBConfig* fbc = glXChooseFBConfig((Display*) win->src.display, DefaultScreen(win->src.display), (i32*) visual_attribs, &fbcount); i32 best_fbc = -1; if (fbcount == 0) { - printf("Failed to find any valid GLX configs\n"); + printf("Failed to find any valid GLX visual configs\n"); return NULL; } @@ -2230,7 +2522,7 @@ Start of Linux / Unix defines XFree(fbc); if (args & RGFW_TRANSPARENT_WINDOW) { - XMatchVisualInfo((Display*) win->src.display, DefaultScreen((Display*) win->src.display), 32, TrueColor, vi); /* for RGBA backgrounds*/ + XMatchVisualInfo((Display*) win->src.display, DefaultScreen((Display*) win->src.display), 32, TrueColor, vi); /*!< for RGBA backgrounds*/ } #else @@ -2241,7 +2533,7 @@ Start of Linux / Unix defines viNorm.depth = 0; XVisualInfo* vi = &viNorm; - XMatchVisualInfo((Display*) win->src.display, DefaultScreen((Display*) win->src.display), 32, TrueColor, vi); /* for RGBA backgrounds*/ + XMatchVisualInfo((Display*) win->src.display, DefaultScreen((Display*) win->src.display), 32, TrueColor, vi); /*!< for RGBA backgrounds*/ #endif /* make X window attrubutes*/ XSetWindowAttributes swa; @@ -2273,16 +2565,19 @@ Start of Linux / Unix defines // with your application - robrohan XClassHint *hint = XAllocClassHint(); assert(hint != NULL); - hint->res_class = "RGFW"; + hint->res_class = (char*)"RGFW"; hint->res_name = (char*)name; // just use the window name as the app name XSetClassHint((Display*) win->src.display, win->src.window, hint); XFree(hint); if ((args & RGFW_NO_INIT_API) == 0) { -#ifdef RGFW_OPENGL +#ifdef RGFW_OPENGL /* This is the second part of setting up opengl. This is where we ask OpenGL for a specific version. */ i32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 }; context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB; - context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + if (RGFW_profile == RGFW_GL_CORE) + context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; + else + context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; if (RGFW_majorVersion || RGFW_minorVersion) { context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB; @@ -2328,7 +2623,7 @@ Start of Linux / Unix defines RGFW_window_setBorder(win, 0); } - XSelectInput((Display*) win->src.display, (Drawable) win->src.window, event_mask); /* tell X11 what events we want*/ + XSelectInput((Display*) win->src.display, (Drawable) win->src.window, event_mask); /*!< tell X11 what events we want*/ /* make it so the user can't close the window until the program does*/ if (wm_delete_window == 0) @@ -2343,10 +2638,10 @@ Start of Linux / Unix defines #endif /* set the background*/ - XStoreName((Display*) win->src.display, (Drawable) win->src.window, name); /* set the name*/ + XStoreName((Display*) win->src.display, (Drawable) win->src.window, name); /*!< set the name*/ XMapWindow((Display*) win->src.display, (Drawable) win->src.window); /* draw the window*/ - XMoveWindow((Display*) win->src.display, (Drawable) win->src.window, win->r.x, win->r.y); /* move the window to it's proper cords*/ + XMoveWindow((Display*) win->src.display, (Drawable) win->src.window, win->r.x, win->r.y); /*!< move the window to it's proper cords*/ if (args & RGFW_ALLOW_DND) { /* init drag and drop atoms and turn on drag and drop for this window */ win->_winArgs |= RGFW_ALLOW_DND; @@ -2373,7 +2668,7 @@ Start of Linux / Unix defines XChangeProperty((Display*) win->src.display, (Window) win->src.window, XdndAware, 4, 32, - PropModeReplace, (u8*) &version, 1); /* turns on drag and drop */ + PropModeReplace, (u8*) &version, 1); /*!< turns on drag and drop */ } #ifdef RGFW_EGL @@ -2421,88 +2716,19 @@ Start of Linux / Unix defines return RGFWMouse; } + typedef struct XDND { + long source, version; + i32 format; + } XDND; /*!< data structure for xdnd events */ + XDND xdnd; - int RGFW_eventWait_forceStop[] = {0, 0, 0}; + int xAxis = 0, yAxis = 0; - void RGFW_stopCheckEvents(void) { - RGFW_eventWait_forceStop[2] = 1; - while (1) { - const char byte = 0; - const ssize_t result = write(RGFW_eventWait_forceStop[1], &byte, 1); - if (result == 1 || result == -1) - break; - } - } + RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { + assert(win != NULL); - void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) { - if (waitMS == 0) - return; - - u8 i; - - if (RGFW_eventWait_forceStop[0] == 0 || RGFW_eventWait_forceStop[1] == 0) { - if (pipe(RGFW_eventWait_forceStop) != -1) { - fcntl(RGFW_eventWait_forceStop[0], F_GETFL, 0); - fcntl(RGFW_eventWait_forceStop[0], F_GETFD, 0); - fcntl(RGFW_eventWait_forceStop[1], F_GETFL, 0); - fcntl(RGFW_eventWait_forceStop[1], F_GETFD, 0); - } - } - - struct pollfd fds[] = { - { ConnectionNumber(win->src.display), POLLIN, 0 }, - { RGFW_eventWait_forceStop[0], POLLIN, 0 }, - #ifdef __linux__ /* blank space for 4 joystick files*/ - { -1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0} - #endif - }; - - u8 index = 2; - - #if defined(__linux__) - for (i = 0; i < RGFW_joystickCount; i++) { - if (RGFW_joysticks[i] == 0) - continue; - - fds[index].fd = RGFW_joysticks[i]; - index++; - } - #endif - - - u64 start = RGFW_getTimeNS(); - - while (XPending(win->src.display) == 0 && waitMS >= -1) { - if (poll(fds, index, waitMS) <= 0) - break; - - if (waitMS > 0) { - waitMS -= (RGFW_getTimeNS() - start) / 1e+6; - } - } - - /* drain any data in the stop request */ - if (RGFW_eventWait_forceStop[2]) { - char data[64]; - read(RGFW_eventWait_forceStop[0], data, sizeof(data)); - - RGFW_eventWait_forceStop[2] = 0; - } - } - - typedef struct XDND { - long source, version; - i32 format; - } XDND; /* data structure for xdnd events */ - XDND xdnd; - - int xAxis = 0, yAxis = 0; - - RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { - assert(win != NULL); - - if (win->event.type == 0) - RGFW_resetKey(); + if (win->event.type == 0) + RGFW_resetKey(); if (win->event.type == RGFW_quit) { return NULL; @@ -2511,52 +2737,14 @@ Start of Linux / Unix defines win->event.type = 0; #ifdef __linux__ - { - u8 i; - for (i = 0; i < RGFW_joystickCount; i++) { - struct js_event e; - - - if (RGFW_joysticks[i] == 0) - continue; - - i32 flags = fcntl(RGFW_joysticks[i], F_GETFL, 0); - fcntl(RGFW_joysticks[i], F_SETFL, flags | O_NONBLOCK); - - ssize_t bytes; - while ((bytes = read(RGFW_joysticks[i], &e, sizeof(e))) > 0) { - switch (e.type) { - case JS_EVENT_BUTTON: - win->event.type = e.value ? RGFW_jsButtonPressed : RGFW_jsButtonReleased; - win->event.button = e.number; - RGFW_jsPressed[i][e.number] = e.value; - RGFW_jsButtonCallback(win, i, e.number, e.value); - return &win->event; - case JS_EVENT_AXIS: - ioctl(RGFW_joysticks[i], JSIOCGAXES, &win->event.axisesCount); - - if ((e.number == 0 || e.number % 2) && e.number != 1) - xAxis = e.value; - else - yAxis = e.value; - - win->event.axis[e.number / 2].x = xAxis; - win->event.axis[e.number / 2].y = yAxis; - win->event.type = RGFW_jsAxisMove; - win->event.joystick = i; - RGFW_jsAxisCallback(win, i, win->event.axis, win->event.axisesCount); - return &win->event; - - default: break; - } - } - } - } + RGFW_Event* event = RGFW_linux_updateJoystick(win); + if (event != NULL) + return event; #endif XPending(win->src.display); - XEvent E; /* raw X11 event */ + XEvent E; /*!< raw X11 event */ /* if there is no unread qued events, get a new one */ if ((QLength(win->src.display) || XEventsQueued((Display*) win->src.display, QueuedAlready) + XEventsQueued((Display*) win->src.display, QueuedAfterReading)) @@ -2573,21 +2761,22 @@ Start of Linux / Unix defines switch (E.type) { case KeyPress: - case KeyRelease: + case KeyRelease: { + win->event.repeat = RGFW_FALSE; /* check if it's a real key release */ if (E.type == KeyRelease && XEventsQueued((Display*) win->src.display, QueuedAfterReading)) { /* get next event if there is one*/ XEvent NE; XPeekEvent((Display*) win->src.display, &NE); if (E.xkey.time == NE.xkey.time && E.xkey.keycode == NE.xkey.keycode) /* check if the current and next are both the same*/ - break; + win->event.repeat = RGFW_TRUE; } /* set event key data */ - KeySym sym = XkbKeycodeToKeysym((Display*) win->src.display, E.xkey.keycode, 0, E.xkey.state & ShiftMask ? 1 : 0); + KeySym sym = (KeySym)XkbKeycodeToKeysym((Display*) win->src.display, E.xkey.keycode, 0, E.xkey.state & ShiftMask ? 1 : 0); win->event.keyCode = RGFW_apiKeyCodeToRGFW(E.xkey.keycode); - char* str = XKeysymToString(sym); + char* str = (char*)XKeysymToString(sym); if (str != NULL) strncpy(win->event.keyName, str, 16); @@ -2602,14 +2791,13 @@ Start of Linux / Unix defines XGetKeyboardControl((Display*) win->src.display, &keystate); RGFW_updateLockState(win, (keystate.led_mask & 1), (keystate.led_mask & 2)); - RGFW_keyboard[win->event.keyCode].current = (E.type == KeyPress); RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, (E.type == KeyPress)); break; - + } case ButtonPress: case ButtonRelease: - win->event.type = E.type; // the events match + win->event.type = RGFW_mouseButtonPressed + (E.type == ButtonRelease); // the events match switch(win->event.button) { case RGFW_mouseScrollUp: @@ -2623,19 +2811,23 @@ Start of Linux / Unix defines win->event.button = E.xbutton.button; RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; + + if (win->event.repeat == RGFW_FALSE) + win->event.repeat = RGFW_isPressed(win, win->event.keyCode); + RGFW_mouseButtons[win->event.button].current = (E.type == ButtonPress); RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, (E.type == ButtonPress)); break; - case MotionNotify: + case MotionNotify: win->event.point.x = E.xmotion.x; win->event.point.y = E.xmotion.y; if ((win->_winArgs & RGFW_HOLD_MOUSE)) { - win->event.point.x = win->_lastMousePoint.x - win->event.point.x; - win->event.point.y = win->_lastMousePoint.y - win->event.point.y; + win->event.point.y = E.xmotion.y; - RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2))); + win->event.point.x = win->_lastMousePoint.x - abs(win->event.point.x); + win->event.point.y = win->_lastMousePoint.y - abs(win->event.point.y); } win->_lastMousePoint = RGFW_POINT(E.xmotion.x, E.xmotion.y); @@ -2668,7 +2860,9 @@ Start of Linux / Unix defines if (XIMaskIsSet(raw->valuators.mask, 1) != 0) deltaY += raw->raw_values[1]; - win->event.point = RGFW_POINT((u32)-deltaX, (u32)-deltaY); + win->event.point = RGFW_POINT((i32)deltaX, (i32)deltaY); + + RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2))); win->event.type = RGFW_mousePosChanged; RGFW_mousePosCallback(win, win->event.point); @@ -2855,7 +3049,7 @@ Start of Linux / Unix defines RGFW_dndInitCallback(win, win->event.point); break; - case SelectionNotify: + case SelectionNotify: { /* this is only for checking for xdnd drops */ if (E.xselection.property != XdndSelection || !(win->_winArgs | RGFW_ALLOW_DND)) break; @@ -2878,7 +3072,7 @@ Start of Linux / Unix defines Copyright (c) 2006-2019 Camilla Löwy */ - const char* prefix = "file://"; + const char* prefix = (const char*)"file://"; char* line; @@ -2945,7 +3139,7 @@ Start of Linux / Unix defines RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); break; - + } case FocusIn: win->event.inFocus = 1; win->event.type = RGFW_focusIn; @@ -3222,7 +3416,7 @@ Start of Linux / Unix defines void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { assert(win != NULL); - + if (mouse > (sizeof(RGFW_mouseIconSrc) / sizeof(u8))) return; @@ -3286,7 +3480,7 @@ Start of Linux / Unix defines *size = sizeN; return s; - } + } /* almost all of this function is sourced from GLFW @@ -3309,27 +3503,25 @@ Start of Linux / Unix defines ATOM_PAIR = XInternAtom((Display*) RGFW_root->src.display, "ATOM_PAIR", False); CLIPBOARD_MANAGER = XInternAtom((Display*) RGFW_root->src.display, "CLIPBOARD_MANAGER", False); } - + XSetSelectionOwner((Display*) RGFW_root->src.display, CLIPBOARD, (Window) RGFW_root->src.window, CurrentTime); XConvertSelection((Display*) RGFW_root->src.display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, (Window) RGFW_root->src.window, CurrentTime); - for (;;) { XEvent event; XNextEvent((Display*) RGFW_root->src.display, &event); - if (event.type != SelectionRequest) - return; + if (event.type != SelectionRequest) { + break; + } const XSelectionRequestEvent* request = &event.xselectionrequest; XEvent reply = { SelectionNotify }; + reply.xselection.property = 0; - char* selectionString = NULL; const Atom formats[] = { UTF8_STRING, XA_STRING }; const i32 formatCount = sizeof(formats) / sizeof(formats[0]); - - selectionString = (char*) text; if (request->target == TARGETS) { const Atom targets[] = { TARGETS, @@ -3350,11 +3542,11 @@ Start of Linux / Unix defines } if (request->target == MULTIPLE) { - Atom* targets; + Atom* targets = NULL; - Atom actualType; - i32 actualFormat; - unsigned long count, bytesAfter; + Atom actualType = 0; + int actualFormat = 0; + unsigned long count = 0, bytesAfter = 0; XGetWindowProperty((Display*) RGFW_root->src.display, request->requestor, request->property, 0, LONG_MAX, False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &targets); @@ -3375,10 +3567,12 @@ Start of Linux / Unix defines targets[i], 8, PropModeReplace, - (u8*) selectionString, + (u8*) text, textLen); - } else + XFlush(RGFW_root->src.display); + } else { targets[i + 1] = None; + } } XChangeProperty((Display*) RGFW_root->src.display, @@ -3390,6 +3584,7 @@ Start of Linux / Unix defines (u8*) targets, count); + XFlush(RGFW_root->src.display); XFree(targets); reply.xselection.property = request->property; @@ -3402,49 +3597,10 @@ Start of Linux / Unix defines reply.xselection.time = request->time; XSendEvent((Display*) RGFW_root->src.display, request->requestor, False, 0, &reply); + XFlush(RGFW_root->src.display); } } - u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) { - assert(win != NULL); - -#ifdef __linux__ - char file[15]; - sprintf(file, "/dev/input/js%i", jsNumber); - - return RGFW_registerJoystickF(win, file); -#endif - } - - u16 RGFW_registerJoystickF(RGFW_window* win, char* file) { - assert(win != NULL); - -#ifdef __linux__ - - i32 js = open(file, O_RDONLY); - - if (js && RGFW_joystickCount < 4) { - RGFW_joystickCount++; - - RGFW_joysticks[RGFW_joystickCount - 1] = open(file, O_RDONLY); - - u8 i; - for (i = 0; i < 16; i++) - RGFW_jsPressed[RGFW_joystickCount - 1][i] = 0; - - } - - else { -#ifdef RGFW_PRINT_ERRORS - RGFW_error = 1; - fprintf(stderr, "Error RGFW_registerJoystickF : Cannot open file %s\n", file); -#endif - } - - return RGFW_joystickCount - 1; -#endif - } - u8 RGFW_window_isFullscreen(RGFW_window* win) { assert(win != NULL); @@ -3648,9 +3804,10 @@ Start of Linux / Unix defines #ifdef RGFW_OPENGL void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { - assert(win != NULL); - - glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx); + if (win == NULL) + glXMakeCurrent((Display*) NULL, (Drawable)NULL, (GLXContext) NULL); + else + glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx); } #endif @@ -3658,8 +3815,6 @@ Start of Linux / Unix defines void RGFW_window_swapBuffers(RGFW_window* win) { assert(win != NULL); - RGFW_window_makeCurrent(win); - /* clear the window*/ if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) { #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) @@ -3693,8 +3848,6 @@ Start of Linux / Unix defines glXSwapBuffers((Display*) win->src.display, (Window) win->src.window); #endif } - - RGFW_window_checkFPS(win); } #if !defined(RGFW_EGL) @@ -3703,9 +3856,9 @@ Start of Linux / Unix defines #if defined(RGFW_OPENGL) ((PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"))((Display*) win->src.display, (Window) win->src.window, swapInterval); + #else + RGFW_UNUSED(swapInterval); #endif - - win->fpsCap = (swapInterval == 1) ? 0 : swapInterval; } #endif @@ -3736,9 +3889,9 @@ Start of Linux / Unix defines RGFW_root = NULL; if ((Drawable) win->src.window) - XDestroyWindow((Display*) win->src.display, (Drawable) win->src.window); /* close the window*/ + XDestroyWindow((Display*) win->src.display, (Drawable) win->src.window); /*!< close the window*/ - XCloseDisplay((Display*) win->src.display); /* kill the display*/ + XCloseDisplay((Display*) win->src.display); /*!< kill the display*/ } #ifdef RGFW_ALLOC_DROPFILES @@ -3757,61 +3910,1083 @@ Start of Linux / Unix defines if (X11Cursorhandle != NULL && RGFW_windowsOpen <= 0) { dlclose(X11Cursorhandle); - X11Cursorhandle = NULL; - } -#endif -#if !defined(RGFW_NO_X11_XI_PRELOAD) - if (X11Xihandle != NULL && RGFW_windowsOpen <= 0) { - dlclose(X11Xihandle); + X11Cursorhandle = NULL; + } +#endif +#if !defined(RGFW_NO_X11_XI_PRELOAD) + if (X11Xihandle != NULL && RGFW_windowsOpen <= 0) { + dlclose(X11Xihandle); + + X11Xihandle = NULL; + } +#endif + + if (RGFW_libxshape != NULL && RGFW_windowsOpen <= 0) { + dlclose(RGFW_libxshape); + RGFW_libxshape = NULL; + } + + if (RGFW_windowsOpen <= 0) { + if (RGFW_eventWait_forceStop[0] || RGFW_eventWait_forceStop[1]){ + close(RGFW_eventWait_forceStop[0]); + close(RGFW_eventWait_forceStop[1]); + } + + u8 i; + for (i = 0; i < RGFW_joystickCount; i++) + close(RGFW_joysticks[i]); + } + + /* set cleared display / window to NULL for error checking */ + win->src.display = (Display*) 0; + win->src.window = (Window) 0; + + RGFW_FREE(win); /*!< free collected window data */ + } + + +/* + End of X11 linux / unix defines +*/ + +#endif /* RGFW_X11 */ + + +/* wayland or X11 defines*/ +#if defined(RGFW_WAYLAND) || defined(RGFW_X11) +#include +#include +#include + u16 RGFW_registerJoystickF(RGFW_window* win, char* file) { + assert(win != NULL); + +#ifdef __linux__ + + i32 js = open(file, O_RDONLY); + + if (js && RGFW_joystickCount < 4) { + RGFW_joystickCount++; + + RGFW_joysticks[RGFW_joystickCount - 1] = open(file, O_RDONLY); + + u8 i; + for (i = 0; i < 16; i++) + RGFW_jsPressed[RGFW_joystickCount - 1][i] = 0; + + } + + else { +#ifdef RGFW_PRINT_ERRORS + RGFW_error = 1; + fprintf(stderr, "Error RGFW_registerJoystickF : Cannot open file %s\n", file); +#endif + } + + return RGFW_joystickCount - 1; +#endif + } + + u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) { + assert(win != NULL); + +#ifdef __linux__ + char file[15]; + sprintf(file, "/dev/input/js%i", jsNumber); + + return RGFW_registerJoystickF(win, file); +#endif + } + + void RGFW_stopCheckEvents(void) { + RGFW_eventWait_forceStop[2] = 1; + while (1) { + const char byte = 0; + const ssize_t result = write(RGFW_eventWait_forceStop[1], &byte, 1); + if (result == 1 || result == -1) + break; + } + } + + void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) { + if (waitMS == 0) + return; + + u8 i; + + if (RGFW_eventWait_forceStop[0] == 0 || RGFW_eventWait_forceStop[1] == 0) { + if (pipe(RGFW_eventWait_forceStop) != -1) { + fcntl(RGFW_eventWait_forceStop[0], F_GETFL, 0); + fcntl(RGFW_eventWait_forceStop[0], F_GETFD, 0); + fcntl(RGFW_eventWait_forceStop[1], F_GETFL, 0); + fcntl(RGFW_eventWait_forceStop[1], F_GETFD, 0); + } + } + + struct pollfd fds[] = { + #ifdef RGFW_WAYLAND + { wl_display_get_fd(win->src.display), POLLIN, 0 }, + #else + { ConnectionNumber(win->src.display), POLLIN, 0 }, + #endif + { RGFW_eventWait_forceStop[0], POLLIN, 0 }, + #ifdef __linux__ /* blank space for 4 joystick files*/ + { -1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0} + #endif + }; + + u8 index = 2; + + #if defined(__linux__) + for (i = 0; i < RGFW_joystickCount; i++) { + if (RGFW_joysticks[i] == 0) + continue; + + fds[index].fd = RGFW_joysticks[i]; + index++; + } + #endif + + + u64 start = RGFW_getTimeNS(); + + #ifdef RGFW_WAYLAND + while (wl_display_dispatch(win->src.display) <= 0 && waitMS >= -1) { + #else + while (XPending(win->src.display) == 0 && waitMS >= -1) { + #endif + if (poll(fds, index, waitMS) <= 0) + break; + + if (waitMS > 0) { + waitMS -= (RGFW_getTimeNS() - start) / 1e+6; + } + } + + /* drain any data in the stop request */ + if (RGFW_eventWait_forceStop[2]) { + char data[64]; + (void)!read(RGFW_eventWait_forceStop[0], data, sizeof(data)); + + RGFW_eventWait_forceStop[2] = 0; + } + } + + u64 RGFW_getTimeNS(void) { + struct timespec ts = { 0 }; + clock_gettime(1, &ts); + unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; + + return nanoSeconds; + } + + u64 RGFW_getTime(void) { + struct timespec ts = { 0 }; + clock_gettime(1, &ts); + unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; + + return (double)(nanoSeconds) * 1e-9; + } +#endif /* end of wayland or X11 time defines*/ + + +/* + + Start of Wayland defines + + +*/ + +#ifdef RGFW_WAYLAND +/* +Wayland TODO: +- fix RGFW_keyPressed lock state + + RGFW_windowMoved, the window was moved (by the user) + RGFW_windowResized the window was resized (by the user), [on webASM this means the browser was resized] + RGFW_windowRefresh The window content needs to be refreshed + + RGFW_dnd a file has been dropped into the window + RGFW_dnd_init + +- window args: + #define RGFW_NO_RESIZE the window cannot be resized by the user + #define RGFW_ALLOW_DND the window supports drag and drop + #define RGFW_SCALE_TO_MONITOR scale the window to the screen + +- other missing functions functions ("TODO wayland") (~30 functions) +- fix buffer rendering weird behavior +*/ + #include + #include + #include + #include + #include + #include + #include + #include + +RGFW_window* RGFW_key_win = NULL; + +void RGFW_eventPipe_push(RGFW_window* win, RGFW_Event event) { + if (win == NULL) { + win = RGFW_key_win; + + if (win == NULL) return; + } + + if (win->src.eventLen >= (i32)(sizeof(win->src.events) / sizeof(win->src.events[0]))) + return; + + win->src.events[win->src.eventLen] = event; + win->src.eventLen += 1; +} + +RGFW_Event RGFW_eventPipe_pop(RGFW_window* win) { + RGFW_Event ev; + ev.type = 0; + + if (win->src.eventLen > -1) + win->src.eventLen -= 1; + + if (win->src.eventLen >= 0) + ev = win->src.events[win->src.eventLen]; + else { + printf("H2\n"); + } + + return ev; +} + +/* wayland global garbage (wayland bad, X11 is fine (ish) (not really)) */ +#include "xdg-shell.h" +#include "xdg-decoration-unstable-v1.h" + +struct xdg_wm_base *xdg_wm_base; +struct wl_compositor* RGFW_compositor = NULL; +struct wl_shm* shm = NULL; +struct wl_shell* RGFW_shell = NULL; +static struct wl_seat *seat = NULL; +static struct xkb_context *xkb_context; +static struct xkb_keymap *keymap = NULL; +static struct xkb_state *xkb_state = NULL; +enum zxdg_toplevel_decoration_v1_mode client_preferred_mode, RGFW_current_mode; +static struct zxdg_decoration_manager_v1 *decoration_manager = NULL; + +struct wl_cursor_theme* RGFW_wl_cursor_theme = NULL; +struct wl_surface* RGFW_cursor_surface = NULL; +struct wl_cursor_image* RGFW_cursor_image = NULL; + +static void xdg_wm_base_ping_handler(void *data, + struct xdg_wm_base *wm_base, uint32_t serial) +{ + RGFW_UNUSED(data); + xdg_wm_base_pong(wm_base, serial); +} + +static const struct xdg_wm_base_listener xdg_wm_base_listener = { + .ping = xdg_wm_base_ping_handler, +}; + +b8 RGFW_wl_configured = 0; + +static void xdg_surface_configure_handler(void *data, + struct xdg_surface *xdg_surface, uint32_t serial) +{ + RGFW_UNUSED(data); + xdg_surface_ack_configure(xdg_surface, serial); + #ifdef RGFW_DEBUG + printf("Surface configured\n"); + #endif + RGFW_wl_configured = 1; +} + +static const struct xdg_surface_listener xdg_surface_listener = { + .configure = xdg_surface_configure_handler, +}; + +static void xdg_toplevel_configure_handler(void *data, + struct xdg_toplevel *toplevel, int32_t width, int32_t height, + struct wl_array *states) +{ + RGFW_UNUSED(data); RGFW_UNUSED(toplevel); RGFW_UNUSED(states) + fprintf(stderr, "XDG toplevel configure: %dx%d\n", width, height); +} + +static void xdg_toplevel_close_handler(void *data, + struct xdg_toplevel *toplevel) +{ + RGFW_UNUSED(data); + RGFW_window* win = (RGFW_window*)xdg_toplevel_get_user_data(toplevel); + if (win == NULL) + win = RGFW_key_win; + + RGFW_Event ev; + ev.type = RGFW_quit; + + RGFW_eventPipe_push(win, ev); + + RGFW_windowQuitCallback(win); +} + +static void shm_format_handler(void *data, + struct wl_shm *shm, uint32_t format) +{ + RGFW_UNUSED(data); RGFW_UNUSED(shm); + fprintf(stderr, "Format %d\n", format); +} + +static const struct wl_shm_listener shm_listener = { + .format = shm_format_handler, +}; + +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + .configure = xdg_toplevel_configure_handler, + .close = xdg_toplevel_close_handler, +}; + +RGFW_window* RGFW_mouse_win = NULL; + +static void pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { + RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(serial); RGFW_UNUSED(surface_x); RGFW_UNUSED(surface_y); + RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); + RGFW_mouse_win = win; + + RGFW_Event ev; + ev.type = RGFW_mouseEnter; + ev.point = win->event.point; + + RGFW_eventPipe_push(win, ev); + + RGFW_mouseNotifyCallBack(win, win->event.point, RGFW_TRUE); +} +static void pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) { + RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(serial); RGFW_UNUSED(surface); + RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); + if (RGFW_mouse_win == win) + RGFW_mouse_win = NULL; + + RGFW_Event ev; + ev.type = RGFW_mouseLeave; + ev.point = win->event.point; + RGFW_eventPipe_push(win, ev); + + RGFW_mouseNotifyCallBack(win, win->event.point, RGFW_FALSE); +} +static void pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) { + RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(x); RGFW_UNUSED(y); + + assert(RGFW_mouse_win != NULL); + + RGFW_Event ev; + ev.type = RGFW_mousePosChanged; + ev.point = RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y)); + RGFW_eventPipe_push(RGFW_mouse_win, ev); + + RGFW_mousePosCallback(RGFW_mouse_win, RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y))); +} +static void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { + RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(serial); + assert(RGFW_mouse_win != NULL); + + u32 b = (button - 0x110) + 1; + + /* flip right and middle button codes */ + if (b == 2) b = 3; + else if (b == 3) b = 2; + + RGFW_mouseButtons[b].prev = RGFW_mouseButtons[b].current; + RGFW_mouseButtons[b].current = state; + + RGFW_Event ev; + ev.type = RGFW_mouseButtonPressed + state; + ev.button = b; + RGFW_eventPipe_push(RGFW_mouse_win, ev); + + RGFW_mouseButtonCallback(RGFW_mouse_win, b, 0, state); +} +static void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { + RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(axis); + assert(RGFW_mouse_win != NULL); + + double scroll = wl_fixed_to_double(value); + + RGFW_Event ev; + ev.type = RGFW_mouseButtonPressed; + ev.button = RGFW_mouseScrollUp + (scroll < 0); + RGFW_eventPipe_push(RGFW_mouse_win, ev); + + RGFW_mouseButtonCallback(RGFW_mouse_win, RGFW_mouseScrollUp + (scroll < 0), scroll, 1); +} + +void RGFW_doNothing(void) { } +static struct wl_pointer_listener pointer_listener = (struct wl_pointer_listener){&pointer_enter, &pointer_leave, &pointer_motion, &pointer_button, &pointer_axis, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing}; + +static void keyboard_keymap (void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size) { + RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(format); + + char *keymap_string = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0); + xkb_keymap_unref (keymap); + keymap = xkb_keymap_new_from_string (xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + + munmap (keymap_string, size); + close (fd); + xkb_state_unref (xkb_state); + xkb_state = xkb_state_new (keymap); +} +static void keyboard_enter (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { + RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(keys); + + RGFW_key_win = (RGFW_window*)wl_surface_get_user_data(surface); + + RGFW_Event ev; + ev.type = RGFW_focusIn; + ev.inFocus = RGFW_TRUE; + RGFW_key_win->event.inFocus = RGFW_TRUE; + + RGFW_eventPipe_push((RGFW_window*)RGFW_mouse_win, ev); + + RGFW_focusCallback(RGFW_key_win, RGFW_TRUE); +} +static void keyboard_leave (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) { + RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); + + RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); + if (RGFW_key_win == win) + RGFW_key_win = NULL; + + RGFW_Event ev; + ev.type = RGFW_focusOut; + ev.inFocus = RGFW_FALSE; + win->event.inFocus = RGFW_FALSE; + RGFW_eventPipe_push(win, ev); + + RGFW_focusCallback(win, RGFW_FALSE); +} +static void keyboard_key (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { + RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time); + + assert(RGFW_key_win != NULL); + + xkb_keysym_t keysym = xkb_state_key_get_one_sym (xkb_state, key+8); + char name[16]; + xkb_keysym_get_name(keysym, name, 16); + + u32 RGFW_key = RGFW_apiKeyCodeToRGFW(key); + RGFW_keyboard[RGFW_key].prev = RGFW_keyboard[RGFW_key].current; + RGFW_keyboard[RGFW_key].current = state; + RGFW_Event ev; + ev.type = RGFW_keyPressed + state; + ev.keyCode = RGFW_key; + strcpy(ev.keyName, name); + ev.repeat = RGFW_isHeld(RGFW_key_win, RGFW_key); + RGFW_eventPipe_push(RGFW_key_win, ev); + + RGFW_updateLockState(RGFW_key_win, xkb_keymap_mod_get_index(keymap, "Lock"), xkb_keymap_mod_get_index(keymap, "Mod2")); + + RGFW_keyCallback(RGFW_key_win, RGFW_key, name, RGFW_key_win->event.lockState, state); +} +static void keyboard_modifiers (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { + RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time); + xkb_state_update_mask (xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); +} +static struct wl_keyboard_listener keyboard_listener = {&keyboard_keymap, &keyboard_enter, &keyboard_leave, &keyboard_key, &keyboard_modifiers, (void*)&RGFW_doNothing}; + +static void seat_capabilities (void *data, struct wl_seat *seat, uint32_t capabilities) { + RGFW_UNUSED(data); + + if (capabilities & WL_SEAT_CAPABILITY_POINTER) { + struct wl_pointer *pointer = wl_seat_get_pointer (seat); + wl_pointer_add_listener (pointer, &pointer_listener, NULL); + } + if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { + struct wl_keyboard *keyboard = wl_seat_get_keyboard (seat); + wl_keyboard_add_listener (keyboard, &keyboard_listener, NULL); + } +} +static struct wl_seat_listener seat_listener = {&seat_capabilities, (void*)&RGFW_doNothing}; + +static void wl_global_registry_handler(void *data, + struct wl_registry *registry, uint32_t id, const char *interface, + uint32_t version) +{ + RGFW_UNUSED(data); RGFW_UNUSED(version); + + if (strcmp(interface, "wl_compositor") == 0) { + RGFW_compositor = wl_registry_bind(registry, + id, &wl_compositor_interface, 4); + } else if (strcmp(interface, "xdg_wm_base") == 0) { + xdg_wm_base = wl_registry_bind(registry, + id, &xdg_wm_base_interface, 1); + } else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) { + decoration_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1); + } else if (strcmp(interface, "wl_shm") == 0) { + shm = wl_registry_bind(registry, + id, &wl_shm_interface, 1); + wl_shm_add_listener(shm, &shm_listener, NULL); + } else if (strcmp(interface,"wl_seat") == 0) { + seat = wl_registry_bind(registry, id, &wl_seat_interface, 1); + wl_seat_add_listener(seat, &seat_listener, NULL); + } + + else { + #ifdef RGFW_DEBUG + printf("did not register %s\n", interface); + return; + #endif + } + + #ifdef RGFW_DEBUG + printf("registered %s\n", interface); + #endif +} + +static void wl_global_registry_remove(void *data, struct wl_registry *registry, uint32_t name) { RGFW_UNUSED(data); RGFW_UNUSED(registry); RGFW_UNUSED(name); } +static const struct wl_registry_listener registry_listener = { + .global = wl_global_registry_handler, + .global_remove = wl_global_registry_remove, +}; + +static const char *get_mode_name(enum zxdg_toplevel_decoration_v1_mode mode) { + switch (mode) { + case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: + return "client-side decorations"; + case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: + return "server-side decorations"; + } + abort(); +} + + +static void decoration_handle_configure(void *data, + struct zxdg_toplevel_decoration_v1 *decoration, + enum zxdg_toplevel_decoration_v1_mode mode) { + RGFW_UNUSED(data); RGFW_UNUSED(decoration); + printf("Using %s\n", get_mode_name(mode)); + RGFW_current_mode = mode; +} + +static const struct zxdg_toplevel_decoration_v1_listener decoration_listener = { + .configure = decoration_handle_configure, +}; + +static void randname(char *buf) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + long r = ts.tv_nsec; + for (int i = 0; i < 6; ++i) { + buf[i] = 'A'+(r&15)+(r&16)*2; + r >>= 5; + } +} + +static int anonymous_shm_open(void) { + char name[] = "/RGFW-wayland-XXXXXX"; + int retries = 100; + + do { + randname(name + strlen(name) - 6); + + --retries; + // shm_open guarantees that O_CLOEXEC is set + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + shm_unlink(name); + return fd; + } + } while (retries > 0 && errno == EEXIST); + + return -1; +} + +int create_shm_file(off_t size) { + int fd = anonymous_shm_open(); + if (fd < 0) { + return fd; + } + + if (ftruncate(fd, size) < 0) { + close(fd); + return -1; + } + + return fd; +} + +static void wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) { + #ifdef RGFW_BUFFER + RGFW_window* win = (RGFW_window*)data; + if ((win->_winArgs & RGFW_NO_CPU_RENDER)) + return; + + #ifndef RGFW_X11_DONT_CONVERT_BGR + u32 x, y; + for (y = 0; y < (u32)win->r.h; y++) { + for (x = 0; x < (u32)win->r.w; x++) { + u32 index = (y * 4 * win->r.w) + x * 4; + + u8 red = win->buffer[index]; + win->buffer[index] = win->buffer[index + 2]; + win->buffer[index + 2] = red; + + } + } + #endif + + wl_surface_attach(win->src.surface, win->src.wl_buffer, 0, 0); + wl_surface_damage_buffer(win->src.surface, 0, 0, win->r.w, win->r.h); + wl_surface_commit(win->src.surface); + #endif +} + +static const struct wl_callback_listener wl_surface_frame_listener = { + .done = wl_surface_frame_done, +}; + + + /* normal wayland RGFW stuff */ + + RGFW_area RGFW_getScreenSize(void) { + RGFW_area area = {}; + + if (RGFW_root != NULL) + /* this isn't right but it's here for buffers */ + area = RGFW_AREA(RGFW_root->r.w, RGFW_root->r.h); + + /* TODO wayland */ + return area; + } + + void RGFW_releaseCursor(RGFW_window* win) { + RGFW_UNUSED(win); + } + + void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { + RGFW_UNUSED(win); RGFW_UNUSED(r); + + /* TODO wayland */ + } + + + RGFWDEF void RGFW_init_buffer(RGFW_window* win); + void RGFW_init_buffer(RGFW_window* win) { + #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + size_t size = win->r.w * win->r.h * 4; + int fd = create_shm_file(size); + if (fd < 0) { + fprintf(stderr, "Failed to create a buffer. size: %ld\n", size); + exit(1); + } + + win->buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (win->buffer == MAP_FAILED) { + fprintf(stderr, "mmap failed!\n"); + close(fd); + exit(1); + } + + struct wl_shm_pool* pool = wl_shm_create_pool(shm, fd, size); + win->src.wl_buffer = wl_shm_pool_create_buffer(pool, 0, win->r.w, win->r.h, win->r.w * 4, + WL_SHM_FORMAT_ARGB8888); + wl_shm_pool_destroy(pool); + + close(fd); + + wl_surface_attach(win->src.surface, win->src.wl_buffer, 0, 0); + wl_surface_commit(win->src.surface); + + u8 color[] = {0x00, 0x00, 0x00, 0xFF}; + + size_t i; + for (i = 0; i < size; i += 4) { + memcpy(&win->buffer[i], color, 4); + } + + #if defined(RGFW_OSMESA) + win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL); + OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); + #endif + #else + RGFW_UNUSED(win); + #endif + } + + + RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { + RGFW_window* win = RGFW_window_basic_init(rect, args); + + fprintf(stderr, "Warning: RGFW Wayland support is experimental\n"); + + win->src.display = wl_display_connect(NULL); + if (win->src.display == NULL) { + #ifdef RGFW_DEBUG + fprintf(stderr, "Failed to load Wayland display\n"); + #endif + return NULL; + } + + struct wl_registry *registry = wl_display_get_registry(win->src.display); + wl_registry_add_listener(registry, ®istry_listener, NULL); + + wl_display_dispatch(win->src.display); + wl_display_roundtrip(win->src.display); + + if (RGFW_compositor == NULL) { + #ifdef RGFW_DEBUG + fprintf(stderr, "Can't find compositor.\n"); + #endif + + return NULL; + } + + if (RGFW_wl_cursor_theme == NULL) { + RGFW_wl_cursor_theme = wl_cursor_theme_load(NULL, 24, shm); + RGFW_cursor_surface = wl_compositor_create_surface(RGFW_compositor); + + struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, "left_ptr"); + RGFW_cursor_image = cursor->images[0]; + struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(RGFW_cursor_image); + + wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0); + wl_surface_commit(RGFW_cursor_surface); + } + + if (RGFW_root == NULL) + xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL); + + xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + + win->src.surface = wl_compositor_create_surface(RGFW_compositor); + wl_surface_set_user_data(win->src.surface, win); + + win->src.xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, win->src.surface); + xdg_surface_add_listener(win->src.xdg_surface, &xdg_surface_listener, NULL); + + xdg_wm_base_set_user_data(xdg_wm_base, win); + + win->src.xdg_toplevel = xdg_surface_get_toplevel(win->src.xdg_surface); + xdg_toplevel_set_user_data(win->src.xdg_toplevel, win); + xdg_toplevel_set_title(win->src.xdg_toplevel, name); + xdg_toplevel_add_listener(win->src.xdg_toplevel, &xdg_toplevel_listener, NULL); + + xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->r.w, win->r.h); + + if (!(args & RGFW_NO_BORDER)) { + win->src.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( + decoration_manager, win->src.xdg_toplevel); + } + + + if (args & RGFW_OPENGL_SOFTWARE) + setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1); + + wl_display_roundtrip(win->src.display); + + wl_surface_commit(win->src.surface); + + /* wait for the surface to be configured */ + while (wl_display_dispatch(win->src.display) != -1 && !RGFW_wl_configured) { } + + + #ifdef RGFW_OPENGL + if ((args & RGFW_NO_INIT_API) == 0) { + win->src.window = wl_egl_window_create(win->src.surface, win->r.w, win->r.h); + RGFW_createOpenGLContext(win); + } + #endif + + RGFW_init_buffer(win); + + struct wl_callback* callback = wl_surface_frame(win->src.surface); + wl_callback_add_listener(callback, &wl_surface_frame_listener, win); + wl_surface_commit(win->src.surface); + + if (args & RGFW_HIDE_MOUSE) { + RGFW_window_showMouse(win, 0); + } + + if (RGFW_root == NULL) { + RGFW_root = win; + } + + win->src.eventIndex = 0; + win->src.eventLen = 0; + + return win; + } + + RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { + if (win->_winArgs & RGFW_WINDOW_HIDE) + return NULL; + + if (win->src.eventIndex == 0) { + if (wl_display_roundtrip(win->src.display) == -1) { + return NULL; + } + RGFW_resetKey(); + } + + #ifdef __linux__ + RGFW_Event* event = RGFW_linux_updateJoystick(win); + if (event != NULL) + return event; + #endif + + if (win->src.eventLen == 0) { + return NULL; + } + + RGFW_Event ev = RGFW_eventPipe_pop(win); + + if (ev.type == 0 || win->event.type == RGFW_quit) { + return NULL; + } + + ev.frameTime = win->event.frameTime; + ev.frameTime2 = win->event.frameTime2; + ev.inFocus = win->event.inFocus; + win->event = ev; + + return &win->event; + } + + + void RGFW_window_resize(RGFW_window* win, RGFW_area a) { + RGFW_UNUSED(win); RGFW_UNUSED(a); + + /* TODO wayland */ + } + + void RGFW_window_move(RGFW_window* win, RGFW_point v) { + RGFW_UNUSED(win); RGFW_UNUSED(v); + + /* TODO wayland */ + } + + void RGFW_window_setIcon(RGFW_window* win, u8* src, RGFW_area a, i32 channels) { + RGFW_UNUSED(win); RGFW_UNUSED(src); RGFW_UNUSED(a); RGFW_UNUSED(channels) + /* TODO wayland */ + } + + void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) { + RGFW_UNUSED(win); RGFW_UNUSED(v); + + /* TODO wayland */ + } + + void RGFW_window_showMouse(RGFW_window* win, i8 show) { + RGFW_UNUSED(win); + + if (show) { + + } + else { + + } + + /* TODO wayland */ + } + + b8 RGFW_window_isMaximized(RGFW_window* win) { + RGFW_UNUSED(win); + /* TODO wayland */ + return 0; + } + + b8 RGFW_window_isMinimized(RGFW_window* win) { + RGFW_UNUSED(win); + /* TODO wayland */ + return 0; + } + + b8 RGFW_window_isHidden(RGFW_window* win) { + RGFW_UNUSED(win); + /* TODO wayland */ + return 0; + } + + b8 RGFW_window_isFullscreen(RGFW_window* win) { + RGFW_UNUSED(win); + /* TODO wayland */ + return 0; + } + + RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { + RGFW_UNUSED(win); + /* TODO wayland */ + return RGFW_POINT(0, 0); + } + + RGFW_point RGFW_getGlobalMousePoint(void) { + /* TODO wayland */ + return RGFW_POINT(0, 0); + } + + void RGFW_window_show(RGFW_window* win) { + //wl_surface_attach(win->src.surface, win->rc., 0, 0); + wl_surface_commit(win->src.surface); + + if (win->_winArgs & RGFW_WINDOW_HIDE) + win->_winArgs ^= RGFW_WINDOW_HIDE; + } + + void RGFW_window_hide(RGFW_window* win) { + wl_surface_attach(win->src.surface, NULL, 0, 0); + wl_surface_commit(win->src.surface); + win->_winArgs |= RGFW_WINDOW_HIDE; + } + + void RGFW_window_setMouseDefault(RGFW_window* win) { + RGFW_UNUSED(win); + + RGFW_window_setMouseStandard(win, RGFW_MOUSE_NORMAL); + } + + void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { + RGFW_UNUSED(win); + + static const char* iconStrings[] = { "left_ptr", "left_ptr", "text", "cross", "pointer", "e-resize", "n-resize", "nw-resize", "ne-resize", "all-resize", "not-allowed" }; + + struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]); + RGFW_cursor_image = cursor->images[0]; + struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(RGFW_cursor_image); + + wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0); + wl_surface_commit(RGFW_cursor_surface); + } + + void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) { + RGFW_UNUSED(win); RGFW_UNUSED(image); RGFW_UNUSED(a); RGFW_UNUSED(channels) + //struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]); + //RGFW_cursor_image = image; + struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(RGFW_cursor_image); + + wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0); + wl_surface_commit(RGFW_cursor_surface); + } + + void RGFW_window_setName(RGFW_window* win, char* name) { + xdg_toplevel_set_title(win->src.xdg_toplevel, name); + } + + void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { + RGFW_UNUSED(win); RGFW_UNUSED(passthrough); + + /* TODO wayland */ + } + + void RGFW_window_setBorder(RGFW_window* win, b8 border) { + RGFW_UNUSED(win); RGFW_UNUSED(border); + + /* TODO wayland */ + } + + void RGFW_window_restore(RGFW_window* win) { + RGFW_UNUSED(win); + + /* TODO wayland */ + } + + void RGFW_window_minimize(RGFW_window* win) { + RGFW_UNUSED(win); + + /* TODO wayland */ + } + + void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { + RGFW_UNUSED(win); RGFW_UNUSED(a); + + /* TODO wayland */ + } + + void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { + RGFW_UNUSED(win); RGFW_UNUSED(a); + + /* TODO wayland */ + } + + RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { + RGFW_monitor m = {}; + RGFW_UNUSED(win); + RGFW_UNUSED(m); + /* TODO wayland */ + + return m; + } + + + #ifndef RGFW_EGL + void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); } + #endif + + void RGFW_window_swapBuffers(RGFW_window* win) { + assert(win != NULL); - X11Xihandle = NULL; + /* clear the window*/ + #ifdef RGFW_BUFFER + wl_surface_frame_done(win, NULL, 0); + if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) + #endif + { + #ifdef RGFW_OPENGL + eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); + #endif } -#endif + + wl_display_flush(win->src.display); + } - if (RGFW_libxshape != NULL && RGFW_windowsOpen <= 0) { - dlclose(RGFW_libxshape); - RGFW_libxshape = NULL; + void RGFW_window_close(RGFW_window* win) { + #ifdef RGFW_EGL + RGFW_closeEGL(win); + #endif + + if (RGFW_root == win) { + RGFW_root = NULL; } - if (RGFW_windowsOpen <= 0) { - if (RGFW_eventWait_forceStop[0] || RGFW_eventWait_forceStop[1]){ - close(RGFW_eventWait_forceStop[0]); - close(RGFW_eventWait_forceStop[1]); - } + xdg_toplevel_destroy(win->src.xdg_toplevel); + xdg_surface_destroy(win->src.xdg_surface); + wl_surface_destroy(win->src.surface); - u8 i; - for (i = 0; i < RGFW_joystickCount; i++) - close(RGFW_joysticks[i]); - } + #ifdef RGFW_BUFFER + wl_buffer_destroy(win->src.wl_buffer); + #endif + + wl_display_disconnect(win->src.display); + RGFW_FREE(win); + } - /* set cleared display / window to NULL for error checking */ - win->src.display = (Display*) 0; - win->src.window = (Window) 0; + RGFW_monitor RGFW_getPrimaryMonitor(void) { + /* TODO wayland */ - RGFW_FREE(win); /* free collected window data */ + return (RGFW_monitor){}; } + + RGFW_monitor* RGFW_getMonitors(void) { + /* TODO wayland */ - u64 RGFW_getTimeNS(void) { - struct timespec ts = { 0 }; - clock_gettime(1, &ts); - unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; + return NULL; + } - return nanoSeconds; + void RGFW_writeClipboard(const char* text, u32 textLen) { + RGFW_UNUSED(text); RGFW_UNUSED(textLen); + + /* TODO wayland */ } - u64 RGFW_getTime(void) { - struct timespec ts = { 0 }; - clock_gettime(1, &ts); - unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; + char* RGFW_readClipboard(size_t* size) { + RGFW_UNUSED(size); - return (double)(nanoSeconds) * 1e-9; + /* TODO wayland */ + + return NULL; } +#endif /* RGFW_WAYLAND */ + /* - End of linux / unix defines + End of Wayland defines */ -#endif /* RGFW_X11 */ - /* @@ -3822,6 +4997,8 @@ Start of Linux / Unix defines #ifdef RGFW_WINDOWS #define WIN32_LEAN_AND_MEAN + #define OEMRESOURCE + #include #include #include @@ -3829,8 +5006,6 @@ Start of Linux / Unix defines #include #include #include - #include - #include #ifndef RGFW_NO_XINPUT typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*); @@ -3873,13 +5048,6 @@ Start of Linux / Unix defines #define wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc - -#if defined(RGFW_DIRECTX) - RGFW_directXinfo RGFW_dxInfo; - - RGFW_directXinfo* RGFW_getDirectXInfo(void) { return &RGFW_dxInfo; } -#endif - void* RGFWjoystickApi = NULL; /* these two wgl functions need to be preloaded */ @@ -3893,7 +5061,6 @@ Start of Linux / Unix defines #define WGL_DRAW_TO_WINDOW_ARB 0x2001 #define WGL_ACCELERATION_ARB 0x2003 #define WGL_NO_ACCELERATION_ARB 0x2025 -#define WGL_SUPPORT_OPENGL_ARB 0x2010 #define WGL_DOUBLE_BUFFER_ARB 0x2011 #define WGL_COLOR_BITS_ARB 0x2014 #define WGL_RED_BITS_ARB 0x2015 @@ -3990,7 +5157,7 @@ static HMODULE wglinstance = NULL; #endif __declspec(dllimport) u32 __stdcall timeBeginPeriod(u32 uPeriod); - + #ifndef RGFW_NO_XINPUT void RGFW_loadXInput(void) { u32 i; @@ -4048,7 +5215,7 @@ static HMODULE wglinstance = NULL; OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); #endif #else -RGFW_UNUSED(win); /* if buffer rendering is not being used */ +RGFW_UNUSED(win); /*!< if buffer rendering is not being used */ #endif } @@ -4056,16 +5223,14 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ DragAcceptFiles(win->src.window, allow); } + void RGFW_releaseCursor(RGFW_window* win) { + ClipCursor(NULL); + const RAWINPUTDEVICE id = { 0x01, 0x02, RIDEV_REMOVE, NULL }; + RegisterRawInputDevices(&id, 1, sizeof(id)); + } + void RGFW_captureCursor(RGFW_window* win, RGFW_rect rect) { RGFW_UNUSED(win) - - if (!rect.x && !rect.y && rect.w && !rect.h) { - ClipCursor(NULL); - const RAWINPUTDEVICE id = { 0x01, 0x02, RIDEV_REMOVE, NULL }; - RegisterRawInputDevices(&id, 1, sizeof(id)); - - return; - } RECT clipRect; GetClientRect(win->src.window, &clipRect); @@ -4101,9 +5266,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ wglGetCurrentContextSRC = (PFN_wglGetCurrentContext) GetProcAddress(wglinstance, "wglGetCurrentContext"); #endif } - - timeBeginPeriod(1); - + if (name[0] == 0) name = (char*) " "; RGFW_eventWindow.r = RGFW_RECT(-1, -1, -1, -1); @@ -4117,7 +5280,12 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ HINSTANCE inh = GetModuleHandleA(NULL); - WNDCLASSA Class = { 0 }; /* Setup the Window class. */ + #ifndef __cplusplus + WNDCLASSA Class = { 0 }; /*!< Setup the Window class. */ + #else + WNDCLASSA Class = { }; + #endif + Class.lpszClassName = name; Class.hInstance = inh; Class.hCursor = LoadCursor(NULL, IDC_ARROW); @@ -4130,7 +5298,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ RECT windowRect, clientRect; if (!(args & RGFW_NO_BORDER)) { - window_style |= WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_VISIBLE | WS_MINIMIZEBOX; + window_style |= WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX; if (!(args & RGFW_NO_RESIZE)) window_style |= WS_SIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME; @@ -4215,17 +5383,26 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ #ifdef RGFW_OPENGL HDC dummy_dc = GetDC(dummyWin); - - PIXELFORMATDESCRIPTOR pfd = { - .nSize = sizeof(pfd), - .nVersion = 1, - .iPixelType = PFD_TYPE_RGBA, - .dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, - .cColorBits = 24, - .cAlphaBits = 8, - .iLayerType = PFD_MAIN_PLANE, - .cDepthBits = 32, - .cStencilBits = 8, + + u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; + + //if (RGFW_DOUBLE_BUFFER) + pfd_flags |= PFD_DOUBLEBUFFER; + + PIXELFORMATDESCRIPTOR pfd = { + sizeof(pfd), + 1, /* version */ + pfd_flags, + PFD_TYPE_RGBA, /* ipixel type */ + 24, /* color bits */ + 0, 0, 0, 0, 0, 0, + 8, /* alpha bits */ + 0, 0, 0, 0, 0, 0, + 32, /* depth bits */ + 8, /* stencil bits */ + 0, + PFD_MAIN_PLANE, /* Layer type */ + 0, 0, 0, 0 }; int pixel_format = ChoosePixelFormat(dummy_dc, &pfd); @@ -4242,41 +5419,41 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ wglMakeCurrent(dummy_dc, 0); wglDeleteContext(dummy_context); ReleaseDC(dummyWin, dummy_dc); - + + /* try to create the pixel format we want for opengl and then try to create an opengl context for the specified version */ if (wglCreateContextAttribsARB != NULL) { - PIXELFORMATDESCRIPTOR pfd = (PIXELFORMATDESCRIPTOR){ sizeof(pfd), 1, PFD_TYPE_RGBA, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, 32, 8, PFD_MAIN_PLANE, 24, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd), 1, pfd_flags, PFD_TYPE_RGBA, 32, 8, PFD_MAIN_PLANE, 24, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; if (args & RGFW_OPENGL_SOFTWARE) pfd.dwFlags |= PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED; if (wglChoosePixelFormatARB != NULL) { - i32* pixel_format_attribs = (i32*)RGFW_initAttribs(args & RGFW_OPENGL_SOFTWARE); + i32* pixel_format_attribs = (i32*)RGFW_initFormatAttribs(args & RGFW_OPENGL_SOFTWARE); int pixel_format; UINT num_formats; wglChoosePixelFormatARB(win->src.hdc, pixel_format_attribs, 0, 1, &pixel_format, &num_formats); if (!num_formats) { - printf("Failed to set the OpenGL 3.3 pixel format.\n"); + printf("Failed to create a pixel format for WGL.\n"); } DescribePixelFormat(win->src.hdc, pixel_format, sizeof(pfd), &pfd); if (!SetPixelFormat(win->src.hdc, pixel_format, &pfd)) { - printf("Failed to set the OpenGL 3.3 pixel format.\n"); + printf("Failed to set the WGL pixel format.\n"); } } - + + /* create opengl/WGL context for the specified version */ u32 index = 0; i32 attribs[40]; - SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB); - - if (RGFW_majorVersion || RGFW_minorVersion) { - SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion); - SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion); + if (RGFW_profile == RGFW_GL_CORE) { + SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB); } - - SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB); - + else { + SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB); + } + if (RGFW_majorVersion || RGFW_minorVersion) { SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion); SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion); @@ -4285,7 +5462,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ SET_ATTRIB(0, 0); win->src.ctx = (HGLRC)wglCreateContextAttribsARB(win->src.hdc, NULL, attribs); - } else { + } else { /* fall back to a default context (probably opengl 2 or something) */ fprintf(stderr, "Failed to create an accelerated OpenGL Context\n"); int pixel_format = ChoosePixelFormat(win->src.hdc, &pfd); @@ -4659,7 +5836,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ CharLowerBuffA(keyName, 16); } } - + RGFW_updateLockState(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001)); strncpy(win->event.keyName, keyName, 16); @@ -4670,6 +5847,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ } win->event.type = RGFW_keyPressed; + win->event.repeat = RGFW_isPressed(win, win->event.keyCode); RGFW_keyboard[win->event.keyCode].current = 1; RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 1); break; @@ -4706,8 +5884,8 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ break; win->event.type = RGFW_mousePosChanged; - win->event.point.x = -raw->data.mouse.lLastX; - win->event.point.y = -raw->data.mouse.lLastY; + win->event.point.x = raw->data.mouse.lLastX; + win->event.point.y = raw->data.mouse.lLastY; break; } @@ -4830,7 +6008,11 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ u8 RGFW_window_isFullscreen(RGFW_window* win) { assert(win != NULL); + #ifndef __cplusplus WINDOWPLACEMENT placement = { 0 }; + #else + WINDOWPLACEMENT placement = { }; + #endif GetWindowPlacement(win->src.window, &placement); return placement.showCmd == SW_SHOWMAXIMIZED; } @@ -4844,7 +6026,11 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ u8 RGFW_window_isMinimized(RGFW_window* win) { assert(win != NULL); + #ifndef __cplusplus WINDOWPLACEMENT placement = { 0 }; + #else + WINDOWPLACEMENT placement = { }; + #endif GetWindowPlacement(win->src.window, &placement); return placement.showCmd == SW_SHOWMINIMIZED; } @@ -4852,7 +6038,11 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ u8 RGFW_window_isMaximized(RGFW_window* win) { assert(win != NULL); + #ifndef __cplusplus WINDOWPLACEMENT placement = { 0 }; + #else + WINDOWPLACEMENT placement = { }; + #endif GetWindowPlacement(win->src.window, &placement); return placement.showCmd == SW_SHOWMAXIMIZED; } @@ -4892,7 +6082,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ for (deviceIndex = 0; EnumDisplayDevicesA(0, (DWORD) deviceIndex, &dd, 0); deviceIndex++) { char* deviceName = dd.DeviceName; if (EnumDisplayDevicesA(deviceName, info.iIndex, &dd, 0)) { - strncpy(monitor.name, dd.DeviceString, 128); /* copy the monitor's name */ + strncpy(monitor.name, dd.DeviceString, 128); /*!< copy the monitor's name */ break; } } @@ -4904,6 +6094,10 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ monitor.rect.h = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; #ifndef RGFW_NO_DPI + #ifndef USER_DEFAULT_SCREEN_DPI + #define USER_DEFAULT_SCREEN_DPI 96 + #endif + if (GetDpiForMonitor != NULL) { u32 x, y; GetDpiForMonitor(src, MDT_ANGULAR_DPI, &x, &y); @@ -4945,8 +6139,12 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ } RGFW_monitor RGFW_getPrimaryMonitor(void) { + #ifdef __cplusplus + return win32CreateMonitor(MonitorFromPoint({ 0, 0 }, MONITOR_DEFAULTTOPRIMARY)); + #else return win32CreateMonitor(MonitorFromPoint((POINT) { 0, 0 }, MONITOR_DEFAULTTOPRIMARY)); - } + #endif + } RGFW_monitor* RGFW_getMonitors(void) { RGFW_mInfo info; @@ -5102,10 +6300,10 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ #endif #ifdef RGFW_OPENGL - wglDeleteContext((HGLRC) win->src.ctx); /* delete opengl context */ + wglDeleteContext((HGLRC) win->src.ctx); /*!< delete opengl context */ #endif - DeleteDC(win->src.hdc); /* delete device context */ - DestroyWindow(win->src.window); /* delete window */ + DeleteDC(win->src.hdc); /*!< delete device context */ + DestroyWindow(win->src.window); /*!< delete window */ #if defined(RGFW_OSMESA) if (win->buffer != NULL) @@ -5292,8 +6490,10 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ #ifdef RGFW_OPENGL void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { - assert(win != NULL); - wglMakeCurrent(win->src.hdc, (HGLRC) win->src.ctx); + if (win == NULL) + wglMakeCurrent(NULL, NULL); + else + wglMakeCurrent(win->src.hdc, (HGLRC) win->src.ctx); } #endif @@ -5308,7 +6508,6 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ if (loadSwapFunc == NULL) { fprintf(stderr, "wglSwapIntervalEXT not supported\n"); - win->fpsCap = (swapInterval == 1) ? 0 : swapInterval; return; } @@ -5319,18 +6518,15 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ if (wglSwapIntervalEXT(swapInterval) == FALSE) fprintf(stderr, "Failed to set swap interval\n"); - #endif - - win->fpsCap = (swapInterval == 1) ? 0 : swapInterval; + #else + RGFW_UNUSED(swapInterval); + #endif } #endif void RGFW_window_swapBuffers(RGFW_window* win) { - assert(win != NULL); - - RGFW_window_makeCurrent(win); - + //assert(win != NULL); /* clear the window*/ if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) { @@ -5356,8 +6552,6 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ win->src.swapchain->lpVtbl->Present(win->src.swapchain, 0, 0); #endif } - - RGFW_window_checkFPS(win); } char* createUTF8FromWideStringWin32(const WCHAR* source) { @@ -5378,20 +6572,28 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ return target; } + + static inline LARGE_INTEGER RGFW_win32_initTimer(void) { + static LARGE_INTEGER frequency = {{0, 0}}; + if (frequency.QuadPart == 0) { + timeBeginPeriod(1); + QueryPerformanceFrequency(&frequency); + } + + return frequency; + } u64 RGFW_getTimeNS(void) { - LARGE_INTEGER frequency; - QueryPerformanceFrequency(&frequency); + LARGE_INTEGER frequency = RGFW_win32_initTimer(); LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - return (u64) (counter.QuadPart * 1e9 / frequency.QuadPart); + return (u64) ((counter.QuadPart * 1e9) / frequency.QuadPart); } u64 RGFW_getTime(void) { - LARGE_INTEGER frequency; - QueryPerformanceFrequency(&frequency); + LARGE_INTEGER frequency = RGFW_win32_initTimer(); LARGE_INTEGER counter; QueryPerformanceCounter(&counter); @@ -5720,11 +6922,9 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ NSWindowStyleMaskHUDWindow = 1 << 13 }; - typedef const char* NSPasteboardType; NSPasteboardType const NSPasteboardTypeString = "public.utf8-plain-text"; // Replaces NSStringPboardType - typedef NS_ENUM(i32, NSDragOperation) { NSDragOperationNone = 0, NSDragOperationCopy = 1, @@ -6002,7 +7202,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); #endif #else - RGFW_UNUSED(win); /* if buffer rendering is not being used */ + RGFW_UNUSED(win); /*!< if buffer rendering is not being used */ #endif } @@ -6063,20 +7263,22 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ #ifdef RGFW_OPENGL if ((args & RGFW_NO_INIT_API) == 0) { - void* attrs = RGFW_initAttribs(args & RGFW_OPENGL_SOFTWARE); + void* attrs = RGFW_initFormatAttribs(args & RGFW_OPENGL_SOFTWARE); void* format = NSOpenGLPixelFormat_initWithAttributes(attrs); if (format == NULL) { - printf("Failed to load pixel format "); + printf("Failed to load pixel format for OpenGL\n"); - void* attrs = RGFW_initAttribs(1); + void* attrs = RGFW_initFormatAttribs(1); format = NSOpenGLPixelFormat_initWithAttributes(attrs); if (format == NULL) printf("and loading software rendering OpenGL failed\n"); else printf("Switching to software rendering\n"); } - + + /* the pixel format can be passed directly to opengl context creation to create a context + this is because the format also includes information about the opengl version (which may be a bad thing) */ win->src.view = NSOpenGLView_initWithFrame((NSRect){{0, 0}, {win->r.w, win->r.h}}, format); objc_msgSend_void(win->src.view, sel_registerName("prepareOpenGL")); win->src.ctx = objc_msgSend_id(win->src.view, sel_registerName("openGLContext")); @@ -6164,6 +7366,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ } // Show the window + objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true); ((id(*)(id, SEL, SEL))objc_msgSend)(win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL); objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true); @@ -6216,7 +7419,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ CGPoint point = CGEventGetLocation(e); CFRelease(e); - return RGFW_POINT((u32) point.x, (u32) point.y); /* the point is loaded during event checks */ + return RGFW_POINT((u32) point.x, (u32) point.y); /*!< the point is loaded during event checks */ } RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { @@ -6340,7 +7543,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); eventPool = objc_msgSend_id(eventPool, sel_registerName("init")); - void* date = (void*) ((id(*)(id, SEL, double))objc_msgSend) + void* date = (void*) ((id(*)(Class, SEL, double))objc_msgSend) (objc_getClass("NSDate"), sel_registerName("dateWithTimeIntervalSinceNow:"), waitMS); NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventMask, void*, NSString*, bool))objc_msgSend) @@ -6411,7 +7614,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ win->event.type = RGFW_mouseEnter; NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow")); - win->event.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y)); + win->event.point = RGFW_POINT((i32) p.x, (i32) (win->r.h - p.y)); RGFW_mouseNotifyCallBack(win, win->event.point, 1); break; } @@ -6429,6 +7632,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ win->event.type = RGFW_keyPressed; char* str = (char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters"))); strncpy(win->event.keyName, str, 16); + win->event.repeat = RGFW_isPressed(win, win->event.keyCode); RGFW_keyboard[win->event.keyCode].current = 1; RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 1); @@ -6501,7 +7705,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ p.x = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaX")); p.y = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaY")); - win->event.point = RGFW_POINT((u32) p.x, (u32) (p.y)); + win->event.point = RGFW_POINT((i32)p.x, (i32)p.y); } RGFW_mousePosCallback(win, win->event.point); @@ -6727,12 +7931,16 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ CGDisplayShowCursor(kCGDirectMainDisplay); objc_msgSend_void(mouse, sel_registerName("set")); } + + void RGFW_releaseCursor(RGFW_window* win) { + CGAssociateMouseAndMouseCursorPosition(1); + } void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win) CGWarpMouseCursorPosition(CGPointMake(r.x + (r.w / 2), r.y + (r.h / 2))); - CGAssociateMouseAndMouseCursorPosition((!r.x && !r.y && r.w && !r.h)); + CGAssociateMouseAndMouseCursorPosition(0); } void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) { @@ -6881,8 +8089,6 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ NSOpenGLContext_setValues(win->src.ctx, &swapInterval, 222); #endif - - win->fpsCap = (swapInterval == 1) ? 0 : swapInterval; } #endif @@ -6910,9 +8116,6 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ void RGFW_window_swapBuffers(RGFW_window* win) { assert(win != NULL); - - RGFW_window_makeCurrent(win); - /* clear the window*/ if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) { @@ -6953,8 +8156,6 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ objc_msgSend_void(win->src.ctx, sel_registerName("flushBuffer")); #endif } - - RGFW_window_checkFPS(win); } void RGFW_window_close(RGFW_window* win) { @@ -7005,23 +8206,14 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ */ /* - Start of Web ASM defines + WEBASM defines */ #ifdef RGFW_WEBASM - - -#define RGFW_jsButtonPressed 7 /*!< a joystick button was pressed */ -#define RGFW_jsButtonReleased 8 /*!< a joystick button was released */ -#define RGFW_jsAxisMove 9 /*!< an axis of a joystick was moved*/ - -#define RGFW_mouseEnter 14 /* mouse entered the window */ -#define RGFW_mouseLeave 15 /* mouse left the window */ - RGFW_Event RGFW_events[20]; size_t RGFW_eventLen = 0; -EM_BOOL on_keydown(int eventType, const EmscriptenKeyboardEvent* e, void* userData) { +EM_BOOL Emscripten_on_keydown(int eventType, const EmscriptenKeyboardEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_events[RGFW_eventLen].type = RGFW_keyPressed; @@ -7032,12 +8224,12 @@ EM_BOOL on_keydown(int eventType, const EmscriptenKeyboardEvent* e, void* userDa RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].prev = RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current; RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current = 1; - RGFW_keyCallback(RGFW_root, e->keyCode, RGFW_events[RGFW_eventLen].keyName, 0, 1); - + RGFW_keyCallback(RGFW_root, RGFW_apiKeyCodeToRGFW(e->keyCode), RGFW_events[RGFW_eventLen].keyName, 0, 1); + return EM_TRUE; } -EM_BOOL on_keyup(int eventType, const EmscriptenKeyboardEvent* e, void* userData) { +EM_BOOL Emscripten_on_keyup(int eventType, const EmscriptenKeyboardEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_events[RGFW_eventLen].type = RGFW_keyReleased; @@ -7049,12 +8241,12 @@ EM_BOOL on_keyup(int eventType, const EmscriptenKeyboardEvent* e, void* userData RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].prev = RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current; RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current = 0; - RGFW_keyCallback(RGFW_root, e->keyCode, RGFW_events[RGFW_eventLen].keyName, 0, 0); + RGFW_keyCallback(RGFW_root, RGFW_apiKeyCodeToRGFW(e->keyCode), RGFW_events[RGFW_eventLen].keyName, 0, 0); return EM_TRUE; } -EM_BOOL on_resize(int eventType, const EmscriptenUiEvent* e, void* userData) { +EM_BOOL Emscripten_on_resize(int eventType, const EmscriptenUiEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_events[RGFW_eventLen].type = RGFW_windowResized; @@ -7064,7 +8256,7 @@ EM_BOOL on_resize(int eventType, const EmscriptenUiEvent* e, void* userData) { return EM_TRUE; } -EM_BOOL on_fullscreenchange(int eventType, const EmscriptenFullscreenChangeEvent* e, void* userData) { +EM_BOOL Emscripten_on_fullscreenchange(int eventType, const EmscriptenFullscreenChangeEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_events[RGFW_eventLen].type = RGFW_windowResized; @@ -7075,7 +8267,7 @@ EM_BOOL on_fullscreenchange(int eventType, const EmscriptenFullscreenChangeEvent return EM_TRUE; } -EM_BOOL on_focusin(int eventType, const EmscriptenFocusEvent* e, void* userData) { +EM_BOOL Emscripten_on_focusin(int eventType, const EmscriptenFocusEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e); RGFW_events[RGFW_eventLen].type = RGFW_focusIn; @@ -7086,7 +8278,7 @@ EM_BOOL on_focusin(int eventType, const EmscriptenFocusEvent* e, void* userData) return EM_TRUE; } -EM_BOOL on_focusout(int eventType, const EmscriptenFocusEvent* e, void* userData) { +EM_BOOL Emscripten_on_focusout(int eventType, const EmscriptenFocusEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e); RGFW_events[RGFW_eventLen].type = RGFW_focusOut; @@ -7097,13 +8289,13 @@ EM_BOOL on_focusout(int eventType, const EmscriptenFocusEvent* e, void* userData return EM_TRUE; } -EM_BOOL on_mousemove(int eventType, const EmscriptenMouseEvent* e, void* userData) { +EM_BOOL Emscripten_on_mousemove(int eventType, const EmscriptenMouseEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_events[RGFW_eventLen].type = RGFW_mousePosChanged; if ((RGFW_root->_winArgs & RGFW_HOLD_MOUSE)) { - RGFW_point p = RGFW_POINT(-e->movementX, -e->movementY); + RGFW_point p = RGFW_POINT(e->movementX, e->movementY); RGFW_events[RGFW_eventLen].point = p; } else @@ -7114,7 +8306,7 @@ EM_BOOL on_mousemove(int eventType, const EmscriptenMouseEvent* e, void* userDat return EM_TRUE; } -EM_BOOL on_mousedown(int eventType, const EmscriptenMouseEvent* e, void* userData) { +EM_BOOL Emscripten_on_mousedown(int eventType, const EmscriptenMouseEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonPressed; @@ -7131,7 +8323,7 @@ EM_BOOL on_mousedown(int eventType, const EmscriptenMouseEvent* e, void* userDat return EM_TRUE; } -EM_BOOL on_mouseup(int eventType, const EmscriptenMouseEvent* e, void* userData) { +EM_BOOL Emscripten_on_mouseup(int eventType, const EmscriptenMouseEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonReleased; @@ -7147,7 +8339,7 @@ EM_BOOL on_mouseup(int eventType, const EmscriptenMouseEvent* e, void* userData) return EM_TRUE; } -EM_BOOL on_wheel(int eventType, const EmscriptenWheelEvent* e, void* userData) { +EM_BOOL Emscripten_on_wheel(int eventType, const EmscriptenWheelEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonPressed; @@ -7164,54 +8356,82 @@ EM_BOOL on_wheel(int eventType, const EmscriptenWheelEvent* e, void* userData) { return EM_TRUE; } -EM_BOOL on_touchstart(int eventType, const EmscriptenTouchEvent* e, void* userData) { +EM_BOOL Emscripten_on_touchstart(int eventType, const EmscriptenTouchEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); - RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonPressed; - RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[0].targetX, e->touches[0].targetY); - RGFW_events[RGFW_eventLen].button = 1; - RGFW_events[RGFW_eventLen].scroll = 0; + size_t i; + for (i = 0; i < (size_t)e->numTouches; i++) { + RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonPressed; + RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); + RGFW_events[RGFW_eventLen].button = 1; + RGFW_events[RGFW_eventLen].scroll = 0; - RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current; - RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 1; + RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current; + RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 1; - RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 1); - RGFW_eventLen++; + RGFW_mousePosCallback(RGFW_root, RGFW_events[RGFW_eventLen].point); + + RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 1); + RGFW_eventLen++; + } return EM_TRUE; } -EM_BOOL on_touchmove(int eventType, const EmscriptenTouchEvent* e, void* userData) { +EM_BOOL Emscripten_on_touchmove(int eventType, const EmscriptenTouchEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + size_t i; + for (i = 0; i < (size_t)e->numTouches; i++) { + RGFW_events[RGFW_eventLen].type = RGFW_mousePosChanged; + RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); + + RGFW_mousePosCallback(RGFW_root, RGFW_events[RGFW_eventLen].point); + RGFW_eventLen++; + } + return EM_TRUE; +} - RGFW_events[RGFW_eventLen].type = RGFW_mousePosChanged; - RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[0].targetX, e->touches[0].targetY); - - RGFW_mousePosCallback(RGFW_root, RGFW_events[RGFW_eventLen].point); - RGFW_eventLen++; - +EM_BOOL Emscripten_on_touchend(int eventType, const EmscriptenTouchEvent* e, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + size_t i; + for (i = 0; i < (size_t)e->numTouches; i++) { + RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonReleased; + RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); + RGFW_events[RGFW_eventLen].button = 1; + RGFW_events[RGFW_eventLen].scroll = 0; + + RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current; + RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 0; + + RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 0); + RGFW_eventLen++; + } return EM_TRUE; } -EM_BOOL on_touchend(int eventType, const EmscriptenTouchEvent* e, void* userData) { +EM_BOOL Emscripten_on_touchcancel(int eventType, const EmscriptenTouchEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e); return EM_TRUE; } + +EM_BOOL Emscripten_on_gamepad(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); - - RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonReleased; - RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[0].targetX, e->touches[0].targetY); - RGFW_events[RGFW_eventLen].button = 1; - RGFW_events[RGFW_eventLen].scroll = 0; - RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current; - RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 0; + if (gamepadEvent->index >= 4) + return 0; - RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 0); - RGFW_eventLen++; + RGFW_joysticks[gamepadEvent->index] = gamepadEvent->connected; - return EM_TRUE; + return 1; // The event was consumed by the callback handler } -EM_BOOL on_touchcancel(int eventType, const EmscriptenTouchEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e); return EM_TRUE; } +void EMSCRIPTEN_KEEPALIVE Emscripten_onDrop(size_t count) { + if (!(RGFW_root->_winArgs & RGFW_ALLOW_DND)) + return; + RGFW_events[RGFW_eventLen].droppedFilesCount = count; + RGFW_dndCallback(RGFW_root, RGFW_events[RGFW_eventLen].droppedFiles, count); + RGFW_eventLen++; +} b8 RGFW_stopCheckEvents_bool = RGFW_FALSE; void RGFW_stopCheckEvents(void) { @@ -7221,6 +8441,9 @@ void RGFW_stopCheckEvents(void) { void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) { RGFW_UNUSED(win); + if (waitMS == 0) + return; + u32 start = (u32)(((u64)RGFW_getTimeNS()) / 1e+6); while ((RGFW_eventLen == 0) && RGFW_stopCheckEvents_bool == RGFW_FALSE && @@ -7244,25 +8467,57 @@ void RGFW_init_buffer(RGFW_window* win) { OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); #endif #else - RGFW_UNUSED(win); /* if buffer rendering is not being used */ + RGFW_UNUSED(win); /*!< if buffer rendering is not being used */ #endif } +void EMSCRIPTEN_KEEPALIVE RGFW_makeSetValue(size_t index, char* file) { + /* This seems like a terrible idea, don't replicate this unless you hate yourself or the OS */ + /* TODO: find a better way to do this, + strcpy doesn't seem to work, maybe because of asyncio + */ + + RGFW_events[RGFW_eventLen].type = RGFW_dnd; + char** arr = (char**)&RGFW_events[RGFW_eventLen].droppedFiles[index]; + *arr = file; +} + +#include +#include +#include + +void EMSCRIPTEN_KEEPALIVE RGFW_mkdir(char* name) { mkdir(name, 0755); } + +void EMSCRIPTEN_KEEPALIVE RGFW_writeFile(const char *path, const char *data, size_t len) { + FILE* file = fopen(path, "w+"); + if (file == NULL) + return; + + fwrite(data, sizeof(char), len, file); + fclose(file); +} + RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { RGFW_UNUSED(name) - RGFW_UNUSED(RGFW_initAttribs); - + RGFW_UNUSED(RGFW_initFormatAttribs); + RGFW_window* win = RGFW_window_basic_init(rect, args); EmscriptenWebGLContextAttributes attrs; attrs.alpha = EM_TRUE; attrs.depth = EM_TRUE; + attrs.alpha = EM_TRUE; attrs.stencil = RGFW_STENCIL; attrs.antialias = RGFW_SAMPLES; attrs.premultipliedAlpha = EM_TRUE; attrs.preserveDrawingBuffer = EM_FALSE; - attrs.renderViaOffscreenBackBuffer = RGFW_AUX_BUFFERS; + + if (RGFW_DOUBLE_BUFFER == 0) + attrs.renderViaOffscreenBackBuffer = 0; + else + attrs.renderViaOffscreenBackBuffer = RGFW_AUX_BUFFERS; + attrs.failIfMajorPerformanceCaveat = EM_FALSE; attrs.majorVersion = (RGFW_majorVersion == 0) ? 1 : RGFW_majorVersion; attrs.minorVersion = RGFW_minorVersion; @@ -7279,22 +8534,79 @@ RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { #endif emscripten_set_canvas_element_size("#canvas", rect.w, rect.h); + emscripten_set_window_title(name); /* load callbacks */ - emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, on_keydown); - emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, on_keyup); - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, on_resize); - emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, EM_FALSE, on_fullscreenchange); - emscripten_set_mousemove_callback("#canvas", NULL, EM_FALSE, on_mousemove); - emscripten_set_touchstart_callback("#canvas", NULL, EM_FALSE, on_touchstart); - emscripten_set_touchend_callback("#canvas", NULL, EM_FALSE, on_touchend); - emscripten_set_touchmove_callback("#canvas", NULL, EM_FALSE, on_touchmove); - emscripten_set_touchcancel_callback("#canvas", NULL, EM_FALSE, on_touchcancel); - emscripten_set_mousedown_callback("#canvas", NULL, EM_FALSE, on_mousedown); - emscripten_set_mouseup_callback("#canvas", NULL, EM_FALSE, on_mouseup); - emscripten_set_wheel_callback("#canvas", NULL, EM_FALSE, on_wheel); - emscripten_set_focusin_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, on_focusin); - emscripten_set_focusout_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, on_focusout); + emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_keydown); + emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_keyup); + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_resize); + emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, EM_FALSE, Emscripten_on_fullscreenchange); + emscripten_set_mousemove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousemove); + emscripten_set_touchstart_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchstart); + emscripten_set_touchend_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchend); + emscripten_set_touchmove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchmove); + emscripten_set_touchcancel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchcancel); + emscripten_set_mousedown_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousedown); + emscripten_set_mouseup_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mouseup); + emscripten_set_wheel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_wheel); + emscripten_set_focusin_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusin); + emscripten_set_focusout_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusout); + emscripten_set_gamepadconnected_callback(NULL, 1, Emscripten_on_gamepad); + emscripten_set_gamepaddisconnected_callback(NULL, 1, Emscripten_on_gamepad); + + if (args & RGFW_ALLOW_DND) { + win->_winArgs |= RGFW_ALLOW_DND; + } + + EM_ASM({ + var canvas = document.getElementById('canvas'); + canvas.addEventListener('drop', function(e) { + e.preventDefault(); + if (e.dataTransfer.file < 0) + return; + + var filenamesArray = []; + var count = e.dataTransfer.files.length; + + /* Read and save the files to emscripten's files */ + var drop_dir = '.rgfw_dropped_files'; + Module._RGFW_mkdir(drop_dir); + + for (var i = 0; i < count; i++) { + var file = e.dataTransfer.files[i]; + + var path = '/' + drop_dir + '/' + file.name.replace("//", '_'); + var reader = new FileReader(); + + reader.onloadend = (e) => { + if (reader.readyState != 2) { + out('failed to read dropped file: '+file.name+': '+reader.error); + } + else { + var data = e.target.result; + + _RGFW_writeFile(path, new Uint8Array(data), file.size); + } + }; + + reader.readAsArrayBuffer(file); + // This works weird on modern opengl + var filename = stringToNewUTF8(path); + + filenamesArray.push(filename); + + Module._RGFW_makeSetValue(i, filename); + } + + Module._Emscripten_onDrop(count); + + for (var i = 0; i < count; ++i) { + _free(filenamesArray[i]); + } + }, true); + + canvas.addEventListener('dragover', function(e) { e.preventDefault(); return false; }, true); + }); RGFW_init_buffer(win); glViewport(0, 0, rect.w, rect.h); @@ -7314,14 +8626,59 @@ RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { static u8 index = 0; - + if (index == 0) RGFW_resetKey(); + /* check gamepads */ + for (int i = 0; (i < emscripten_get_num_gamepads()) && (i < 4); i++) { + if (RGFW_joysticks[i] == 0) + continue;; + + EmscriptenGamepadEvent gamepadState; + + if (emscripten_get_gamepad_status(i, &gamepadState) != EMSCRIPTEN_RESULT_SUCCESS) + break; + + // Register buttons data for every connected gamepad + for (int j = 0; (j < gamepadState.numButtons) && (j < 16); j++) { + u32 map[] = { + RGFW_JS_A, RGFW_JS_X, RGFW_JS_B, RGFW_JS_Y, + RGFW_JS_L1, RGFW_JS_R1, RGFW_JS_L2, RGFW_JS_R2, + RGFW_JS_SELECT, RGFW_JS_START, + 0, 0, + RGFW_JS_UP, RGFW_JS_DOWN, RGFW_JS_LEFT, RGFW_JS_RIGHT + }; + + u32 button = map[j]; + if (RGFW_jsPressed[i][button] != gamepadState.digitalButton[j]) { + win->event.type = RGFW_jsButtonPressed; + win->event.joystick = i; + win->event.button = map[j]; + return &win->event; + } + + RGFW_jsPressed[i][button] = gamepadState.digitalButton[j]; + } + + for (int j = 0; (j < gamepadState.numAxes) && (j < 4); j += 2) { + win->event.axisesCount = gamepadState.numAxes; + if (win->event.axis[j].x != gamepadState.axis[j] || + win->event.axis[j].y != gamepadState.axis[j + 1] + ) { + win->event.axis[j].x = gamepadState.axis[j]; + win->event.axis[j].y = gamepadState.axis[j + 1]; + win->event.type = RGFW_jsAxisMove; + win->event.joystick = i; + return &win->event; + } + } + } + + /* check queued events */ if (RGFW_eventLen == 0) return NULL; - RGFW_events[index].fps = win->event.fps; RGFW_events[index].frameTime = win->event.frameTime; RGFW_events[index].frameTime2 = win->event.frameTime2; RGFW_events[index].inFocus = win->event.inFocus; @@ -7389,6 +8746,14 @@ RGFW_point RGFW_getGlobalMousePoint(void) { return point; } +RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { + RGFW_UNUSED(win); + + EmscriptenMouseEvent mouseEvent; + emscripten_get_mouse_status(&mouseEvent); + return RGFW_POINT( mouseEvent.targetX, mouseEvent.targetY); +} + void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { RGFW_UNUSED(win); @@ -7417,13 +8782,15 @@ char* RGFW_readClipboard(size_t* size) { if (size != NULL) *size = 0; - char* str = malloc(1); + char* str = (char*)malloc(1); str[0] = '\0'; return str; } void RGFW_window_swapBuffers(RGFW_window* win) { + RGFW_UNUSED(win); + #ifdef RGFW_BUFFER if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) { glEnable(GL_TEXTURE_2D); @@ -7431,9 +8798,6 @@ void RGFW_window_swapBuffers(RGFW_window* win) { GLuint texture; glGenTextures(1,&texture); - //glPixelStorei( GL_PACK_ROW_LENGTH, RGFW_bufferSize.w); - //glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, RGFW_bufferSize.h); - glBindTexture(GL_TEXTURE_2D,texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -7464,23 +8828,19 @@ void RGFW_window_swapBuffers(RGFW_window* win) { #endif emscripten_webgl_commit_frame(); - - if (win->fpsCap == 0 || win->fpsCap < 100) { - emscripten_sleep(0); - } - - RGFW_window_checkFPS(win); + emscripten_sleep(0); } void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { - emscripten_webgl_make_context_current(win->src.ctx); + if (win == NULL) + emscripten_webgl_make_context_current(0); + else + emscripten_webgl_make_context_current(win->src.ctx); } #ifndef RGFW_EGL -void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { - win->fpsCap = (swapInterval == 1) ? 0 : swapInterval; -} +void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); } #endif void RGFW_window_close(RGFW_window* win) { @@ -7512,16 +8872,22 @@ u64 RGFW_getTime(void) { return emscripten_get_now() * 1000; } +void RGFW_releaseCursor(RGFW_window* win) { + emscripten_exit_pointerlock(); +} + void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win) - if (!r.x && !r.y && !r.w && !r.h) { - emscripten_exit_pointerlock(); - return; - } emscripten_request_pointerlock("#canvas", 1); } + +void RGFW_window_setName(RGFW_window* win, char* name) { + RGFW_UNUSED(win); + emscripten_set_window_title(name); +} + /* unsupported functions */ RGFW_monitor* RGFW_getMonitors(void) { return NULL; } RGFW_monitor RGFW_getPrimaryMonitor(void) { return (RGFW_monitor){}; } @@ -7531,8 +8897,6 @@ void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win) RG void RGFW_window_minimize(RGFW_window* win) { RGFW_UNUSED(win)} void RGFW_window_restore(RGFW_window* win) { RGFW_UNUSED(win) } void RGFW_window_setBorder(RGFW_window* win, b8 border) { RGFW_UNUSED(win) RGFW_UNUSED(border) } -void RGFW_window_setDND(RGFW_window* win, b8 allow) { RGFW_UNUSED(win) RGFW_UNUSED(allow) } -void RGFW_window_setName(RGFW_window* win, char* name) { RGFW_UNUSED(win) RGFW_UNUSED(name) } void RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) { RGFW_UNUSED(win) RGFW_UNUSED(icon) RGFW_UNUSED(a) RGFW_UNUSED(channels) } void RGFW_window_hide(RGFW_window* win) { RGFW_UNUSED(win) } void RGFW_window_show(RGFW_window* win) {RGFW_UNUSED(win) } @@ -7546,7 +8910,7 @@ RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { RGFW_UNUSED(win) return /* end of web asm defines */ /* unix (macOS, linux, web asm) only stuff */ -#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WEBASM) +#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WEBASM) || defined(RGFW_WAYLAND) /* unix threading */ #ifndef RGFW_NO_THREADS #include @@ -7561,7 +8925,7 @@ RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { RGFW_UNUSED(win) return void RGFW_cancelThread(RGFW_thread thread) { pthread_cancel((pthread_t) thread); } void RGFW_joinThread(RGFW_thread thread) { pthread_join((pthread_t) thread, NULL); } #ifdef __linux__ - void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { pthread_setschedprio(thread, priority); } + void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { pthread_setschedprio((pthread_t)thread, priority); } #endif #endif @@ -7579,6 +8943,6 @@ RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { RGFW_UNUSED(win) return #endif /* end of unix / mac stuff*/ #endif /*RGFW_IMPLEMENTATION*/ -#ifdef __cplusplus +#if defined(__cplusplus) && !defined(__EMSCRIPTEN__) } #endif diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index d98c6391db71..afea0c2e6f6a 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1239,15 +1239,15 @@ int InitPlatform(void) // Check selection OpenGL version if (rlGetVersion() == RL_OPENGL_21) { - RGFW_setGLVersion(2, 1); + RGFW_setGLVersion(RGFW_GL_CORE, 2, 1); } else if (rlGetVersion() == RL_OPENGL_33) { - RGFW_setGLVersion(3, 3); + RGFW_setGLVersion(RGFW_GL_CORE, 3, 3); } else if (rlGetVersion() == RL_OPENGL_43) { - RGFW_setGLVersion(4, 1); + RGFW_setGLVersion(RGFW_GL_CORE, 4, 1); } if (CORE.Window.flags & FLAG_MSAA_4X_HINT) From 7fab03c0b4f079a6450f03d82ca17f2e18be3477 Mon Sep 17 00:00:00 2001 From: hanaxars <156359933+hanaxars@users.noreply.github.com> Date: Mon, 19 Aug 2024 14:41:20 +0300 Subject: [PATCH 37/47] Fix warnings (#4264) Fix following gcc warnings when SVG enabled: rtextures.c: In function 'LoadImageSvg': rtextures.c:374:52: warning: pointer targets in passing argument 1 of 'nsvgParse' differ in signedness [-Wpointer-sign] 374 | struct NSVGimage *svgImage = nsvgParse(fileData, "px", 96.0f); | ^~~~~~~~ | | | unsigned char * In file included from rtextures.c:230: external/nanosvg.h:2952:28: note: expected 'char *' but argument is of type 'unsigned char *' 2952 | NSVGimage* nsvgParse(char* input, const char* units, float dpi) | ~~~~~~^~~~~ rtextures.c:407:43: warning: comparison of distinct pointer types lacks a cast [-Wcompare-distinct-pointer-types] 407 | if (isSvgStringValid && (fileData != fileNameOrString)) UnloadFileData(fileData); | ^~ rtextures.c: In function 'LoadImageFromMemory': rtextures.c:614:52: warning: passing argument 1 of 'nsvgParse' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers] 614 | struct NSVGimage *svgImage = nsvgParse(fileData, "px", 96.0f); | ^~~~~~~~ external/nanosvg.h:2952:28: note: expected 'char *' but argument is of type 'const unsigned char *' 2952 | NSVGimage* nsvgParse(char* input, const char* units, float dpi) | ~~~~~~^~~~~ --- src/rtextures.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 5fa01c9dff2d..f2faa18503a0 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -371,7 +371,7 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) if (isSvgStringValid) { - struct NSVGimage *svgImage = nsvgParse(fileData, "px", 96.0f); + struct NSVGimage *svgImage = nsvgParse((char *)fileData, "px", 96.0f); unsigned char *img = RL_MALLOC(width*height*4); @@ -404,7 +404,7 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) nsvgDeleteRasterizer(rast); } - if (isSvgStringValid && (fileData != fileNameOrString)) UnloadFileData(fileData); + if (isSvgStringValid && (fileData != (unsigned char *)fileNameOrString)) UnloadFileData(fileData); } #else TRACELOG(LOG_WARNING, "SVG image support not enabled, image can not be loaded"); @@ -611,7 +611,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i (fileData[2] == 'v') && (fileData[3] == 'g')) { - struct NSVGimage *svgImage = nsvgParse(fileData, "px", 96.0f); + struct NSVGimage *svgImage = nsvgParse((char *)fileData, "px", 96.0f); unsigned char *img = RL_MALLOC(svgImage->width*svgImage->height*4); // Rasterize From 039df36f4b73cfd685c143f47e71abf93270e709 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 20 Aug 2024 19:13:18 +0200 Subject: [PATCH 38/47] REVIEWED: Automation events mouse wheel #4263 --- examples/core/core_automation_events.c | 70 +++++++++++++------------- src/rcore.c | 7 +-- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c index 319b013bad05..64ca51fda784 100644 --- a/examples/core/core_automation_events.c +++ b/examples/core/core_automation_events.c @@ -88,7 +88,7 @@ int main(void) // Update //---------------------------------------------------------------------------------- float deltaTime = 0.015f;//GetFrameTime(); - + // Dropped files logic //---------------------------------------------------------------------------------- if (IsFileDropped()) @@ -157,11 +157,6 @@ int main(void) } else player.canJump = true; - camera.zoom += ((float)GetMouseWheelMove()*0.05f); - - if (camera.zoom > 3.0f) camera.zoom = 3.0f; - else if (camera.zoom < 0.25f) camera.zoom = 0.25f; - if (IsKeyPressed(KEY_R)) { // Reset game state @@ -176,12 +171,44 @@ int main(void) } //---------------------------------------------------------------------------------- + // Events playing + // NOTE: Logic must be before Camera update because it depends on mouse-wheel value, + // that can be set by the played event... but some other inputs could be affected + //---------------------------------------------------------------------------------- + if (eventPlaying) + { + // NOTE: Multiple events could be executed in a single frame + while (playFrameCounter == aelist.events[currentPlayFrame].frame) + { + PlayAutomationEvent(aelist.events[currentPlayFrame]); + currentPlayFrame++; + + if (currentPlayFrame == aelist.count) + { + eventPlaying = false; + currentPlayFrame = 0; + playFrameCounter = 0; + + TraceLog(LOG_INFO, "FINISH PLAYING!"); + break; + } + } + + playFrameCounter++; + } + //---------------------------------------------------------------------------------- + // Update camera //---------------------------------------------------------------------------------- camera.target = player.position; camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f }; float minX = 1000, minY = 1000, maxX = -1000, maxY = -1000; + // WARNING: On event replay, mouse-wheel internal value is set + camera.zoom += ((float)GetMouseWheelMove()*0.05f); + if (camera.zoom > 3.0f) camera.zoom = 3.0f; + else if (camera.zoom < 0.25f) camera.zoom = 0.25f; + for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++) { EnvElement *element = &envElements[i]; @@ -200,8 +227,8 @@ int main(void) if (min.y > 0) camera.offset.y = screenHeight/2 - min.y; //---------------------------------------------------------------------------------- - // Toggle events recording - if (IsKeyPressed(KEY_S)) + // Events management + if (IsKeyPressed(KEY_S)) // Toggle events recording { if (!eventPlaying) { @@ -222,7 +249,7 @@ int main(void) } } } - else if (IsKeyPressed(KEY_A)) + else if (IsKeyPressed(KEY_A)) // Toggle events playing (WARNING: Starts next frame) { if (!eventRecording && (aelist.count > 0)) { @@ -241,32 +268,7 @@ int main(void) camera.zoom = 1.0f; } } - - if (eventPlaying) - { - // NOTE: Multiple events could be executed in a single frame - while (playFrameCounter == aelist.events[currentPlayFrame].frame) - { - TraceLog(LOG_INFO, "PLAYING: PlayFrameCount: %i | currentPlayFrame: %i | Event Frame: %i, param: %i", - playFrameCounter, currentPlayFrame, aelist.events[currentPlayFrame].frame, aelist.events[currentPlayFrame].params[0]); - - PlayAutomationEvent(aelist.events[currentPlayFrame]); - currentPlayFrame++; - if (currentPlayFrame == aelist.count) - { - eventPlaying = false; - currentPlayFrame = 0; - playFrameCounter = 0; - - TraceLog(LOG_INFO, "FINISH PLAYING!"); - break; - } - } - - playFrameCounter++; - } - if (eventRecording || eventPlaying) frameCounter++; else frameCounter = 0; //---------------------------------------------------------------------------------- diff --git a/src/rcore.c b/src/rcore.c index 5485ce8ce032..e8041ea4198c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -669,7 +669,6 @@ void InitWindow(int width, int height, const char *title) #endif #endif - CORE.Time.frameCounter = 0; CORE.Window.shouldClose = false; @@ -2707,8 +2706,8 @@ void PlayAutomationEvent(AutomationEvent event) } break; case INPUT_MOUSE_WHEEL_MOTION: // param[0]: x delta, param[1]: y delta { - CORE.Input.Mouse.currentWheelMove.x = (float)event.params[0]; break; - CORE.Input.Mouse.currentWheelMove.y = (float)event.params[1]; break; + CORE.Input.Mouse.currentWheelMove.x = (float)event.params[0]; + CORE.Input.Mouse.currentWheelMove.y = (float)event.params[1]; } break; case INPUT_TOUCH_UP: CORE.Input.Touch.currentTouchState[event.params[0]] = false; break; // param[0]: id case INPUT_TOUCH_DOWN: CORE.Input.Touch.currentTouchState[event.params[0]] = true; break; // param[0]: id @@ -2745,6 +2744,8 @@ void PlayAutomationEvent(AutomationEvent event) case ACTION_SETTARGETFPS: SetTargetFPS(event.params[0]); break; default: break; } + + TRACELOG(LOG_INFO, "AUTOMATION PLAY: Frame: %i | Event type: %i | Event parameters: %i, %i, %i", event.frame, event.type, event.params[0], event.params[1], event.params[2]); } #endif } From fa3f73d88174fb66d9b275e2970e4d201e6a9ecb Mon Sep 17 00:00:00 2001 From: Paperdomo101 <38697390+Paperdomo101@users.noreply.github.com> Date: Wed, 21 Aug 2024 20:07:52 +0800 Subject: [PATCH 39/47] [rshapes] Review DrawGradient color parameter names (#4270) void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2); void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2); void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); Have been changed to: void DrawCircleGradient(int centerX, int centerY, float radius, Color inner, Color outer); void DrawRectangleGradientV(int posX, int posY, int width, int height, Color top, Color bottom); void DrawRectangleGradientH(int posX, int posY, int width, int height, Color left, Color right); void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight); --- src/raylib.h | 8 ++++---- src/rshapes.c | 30 +++++++++++++----------------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 1cf34f004470..0759c259f18e 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1238,7 +1238,7 @@ RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color c RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline -RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle +RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color inner, Color outer); // Draw a gradient-filled circle RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline RLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color); // Draw circle outline (Vector version) @@ -1250,9 +1250,9 @@ RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color) RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version) RLAPI void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color); // Draw a color-filled rectangle with pro parameters -RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a vertical-gradient-filled rectangle -RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a horizontal-gradient-filled rectangle -RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // Draw a gradient-filled rectangle with custom vertex colors +RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color top, Color bottom); // Draw a vertical-gradient-filled rectangle +RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color left, Color right); // Draw a horizontal-gradient-filled rectangle +RLAPI void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight); // Draw a gradient-filled rectangle with custom vertex colors RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline RLAPI void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color); // Draw rectangle outline with extended parameters RLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle with rounded edges diff --git a/src/rshapes.c b/src/rshapes.c index 676b2521a06b..66dfbf3b606a 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -430,17 +430,16 @@ void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float } // Draw a gradient-filled circle -// NOTE: Gradient goes from center (color1) to border (color2) -void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2) +void DrawCircleGradient(int centerX, int centerY, float radius, Color inner, Color outer) { rlBegin(RL_TRIANGLES); for (int i = 0; i < 360; i += 10) { - rlColor4ub(color1.r, color1.g, color1.b, color1.a); + rlColor4ub(inner.r, inner.g, inner.b, inner.a); rlVertex2f((float)centerX, (float)centerY); - rlColor4ub(color2.r, color2.g, color2.b, color2.a); + rlColor4ub(outer.r, outer.g, outer.b, outer.a); rlVertex2f((float)centerX + cosf(DEG2RAD*(i + 10))*radius, (float)centerY + sinf(DEG2RAD*(i + 10))*radius); - rlColor4ub(color2.r, color2.g, color2.b, color2.a); + rlColor4ub(outer.r, outer.g, outer.b, outer.a); rlVertex2f((float)centerX + cosf(DEG2RAD*i)*radius, (float)centerY + sinf(DEG2RAD*i)*radius); } rlEnd(); @@ -761,22 +760,19 @@ void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color } // Draw a vertical-gradient-filled rectangle -// NOTE: Gradient goes from bottom (color1) to top (color2) -void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2) +void DrawRectangleGradientV(int posX, int posY, int width, int height, Color top, Color bottom) { - DrawRectangleGradientEx((Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, color1, color2, color2, color1); + DrawRectangleGradientEx((Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, top, bottom, bottom, top); } // Draw a horizontal-gradient-filled rectangle -// NOTE: Gradient goes from bottom (color1) to top (color2) -void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2) +void DrawRectangleGradientH(int posX, int posY, int width, int height, Color left, Color right) { - DrawRectangleGradientEx((Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, color1, color1, color2, color2); + DrawRectangleGradientEx((Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, left, left, right, right); } // Draw a gradient-filled rectangle -// NOTE: Colors refer to corners, starting at top-lef corner and counter-clockwise -void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4) +void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight) { rlSetTexture(GetShapesTexture().id); Rectangle shapeRect = GetShapesTextureRectangle(); @@ -785,19 +781,19 @@ void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, rlNormal3f(0.0f, 0.0f, 1.0f); // NOTE: Default raylib font character 95 is a white square - rlColor4ub(col1.r, col1.g, col1.b, col1.a); + rlColor4ub(topLeft.r, topLeft.g, topLeft.b, topLeft.a); rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); rlVertex2f(rec.x, rec.y); - rlColor4ub(col2.r, col2.g, col2.b, col2.a); + rlColor4ub(bottomLeft.r, bottomLeft.g, bottomLeft.b, bottomLeft.a); rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); rlVertex2f(rec.x, rec.y + rec.height); - rlColor4ub(col3.r, col3.g, col3.b, col3.a); + rlColor4ub(topRight.r, topRight.g, topRight.b, topRight.a); rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); rlVertex2f(rec.x + rec.width, rec.y + rec.height); - rlColor4ub(col4.r, col4.g, col4.b, col4.a); + rlColor4ub(bottomRight.r, bottomRight.g, bottomRight.b, bottomRight.a); rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); rlVertex2f(rec.x + rec.width, rec.y); rlEnd(); From bae0af241ed8ecf0b8107d3d3c43fef297e038d3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 21 Aug 2024 12:08:09 +0000 Subject: [PATCH 40/47] Update raylib_api.* by CI --- parser/output/raylib_api.json | 20 ++++++++++---------- parser/output/raylib_api.lua | 20 ++++++++++---------- parser/output/raylib_api.txt | 20 ++++++++++---------- parser/output/raylib_api.xml | 20 ++++++++++---------- 4 files changed, 40 insertions(+), 40 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index f149d628f0f1..c4ce91e87f91 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -5485,11 +5485,11 @@ }, { "type": "Color", - "name": "color1" + "name": "inner" }, { "type": "Color", - "name": "color2" + "name": "outer" } ] }, @@ -5785,11 +5785,11 @@ }, { "type": "Color", - "name": "color1" + "name": "top" }, { "type": "Color", - "name": "color2" + "name": "bottom" } ] }, @@ -5816,11 +5816,11 @@ }, { "type": "Color", - "name": "color1" + "name": "left" }, { "type": "Color", - "name": "color2" + "name": "right" } ] }, @@ -5835,19 +5835,19 @@ }, { "type": "Color", - "name": "col1" + "name": "topLeft" }, { "type": "Color", - "name": "col2" + "name": "bottomLeft" }, { "type": "Color", - "name": "col3" + "name": "topRight" }, { "type": "Color", - "name": "col4" + "name": "bottomRight" } ] }, diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 7c00fff33f29..3a9e2f04f076 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -4697,8 +4697,8 @@ return { {type = "int", name = "centerX"}, {type = "int", name = "centerY"}, {type = "float", name = "radius"}, - {type = "Color", name = "color1"}, - {type = "Color", name = "color2"} + {type = "Color", name = "inner"}, + {type = "Color", name = "outer"} } }, { @@ -4835,8 +4835,8 @@ return { {type = "int", name = "posY"}, {type = "int", name = "width"}, {type = "int", name = "height"}, - {type = "Color", name = "color1"}, - {type = "Color", name = "color2"} + {type = "Color", name = "top"}, + {type = "Color", name = "bottom"} } }, { @@ -4848,8 +4848,8 @@ return { {type = "int", name = "posY"}, {type = "int", name = "width"}, {type = "int", name = "height"}, - {type = "Color", name = "color1"}, - {type = "Color", name = "color2"} + {type = "Color", name = "left"}, + {type = "Color", name = "right"} } }, { @@ -4858,10 +4858,10 @@ return { returnType = "void", params = { {type = "Rectangle", name = "rec"}, - {type = "Color", name = "col1"}, - {type = "Color", name = "col2"}, - {type = "Color", name = "col3"}, - {type = "Color", name = "col4"} + {type = "Color", name = "topLeft"}, + {type = "Color", name = "bottomLeft"}, + {type = "Color", name = "topRight"}, + {type = "Color", name = "bottomRight"} } }, { diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 73170c860a46..afa9b525dba0 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -2176,8 +2176,8 @@ Function 217: DrawCircleGradient() (5 input parameters) Param[1]: centerX (type: int) Param[2]: centerY (type: int) Param[3]: radius (type: float) - Param[4]: color1 (type: Color) - Param[5]: color2 (type: Color) + Param[4]: inner (type: Color) + Param[5]: outer (type: Color) Function 218: DrawCircleV() (3 input parameters) Name: DrawCircleV Return type: void @@ -2278,8 +2278,8 @@ Function 229: DrawRectangleGradientV() (6 input parameters) Param[2]: posY (type: int) Param[3]: width (type: int) Param[4]: height (type: int) - Param[5]: color1 (type: Color) - Param[6]: color2 (type: Color) + Param[5]: top (type: Color) + Param[6]: bottom (type: Color) Function 230: DrawRectangleGradientH() (6 input parameters) Name: DrawRectangleGradientH Return type: void @@ -2288,17 +2288,17 @@ Function 230: DrawRectangleGradientH() (6 input parameters) Param[2]: posY (type: int) Param[3]: width (type: int) Param[4]: height (type: int) - Param[5]: color1 (type: Color) - Param[6]: color2 (type: Color) + Param[5]: left (type: Color) + Param[6]: right (type: Color) Function 231: DrawRectangleGradientEx() (5 input parameters) Name: DrawRectangleGradientEx Return type: void Description: Draw a gradient-filled rectangle with custom vertex colors Param[1]: rec (type: Rectangle) - Param[2]: col1 (type: Color) - Param[3]: col2 (type: Color) - Param[4]: col3 (type: Color) - Param[5]: col4 (type: Color) + Param[2]: topLeft (type: Color) + Param[3]: bottomLeft (type: Color) + Param[4]: topRight (type: Color) + Param[5]: bottomRight (type: Color) Function 232: DrawRectangleLines() (5 input parameters) Name: DrawRectangleLines Return type: void diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index 474bc0473713..8a6560f489bc 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -1353,8 +1353,8 @@ - - + + @@ -1431,23 +1431,23 @@ - - + + - - + + - - - - + + + + From c8bee7c439d3a49d63ab0e67441fffb56ea4bf2a Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Wed, 21 Aug 2024 08:11:59 -0700 Subject: [PATCH 41/47] [rmodels] Add a warning when loading an OBJ with multiple materials. (#4271) * Update raylib_api.* by CI * Add a temp warning about material assignments during OBJ loading if the file has more than one material. To be replaced when the OBJ translation code is fixed. --------- Co-authored-by: github-actions[bot] --- src/rmodels.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rmodels.c b/src/rmodels.c index 36a7e254d1f3..8afa3df574c5 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4092,6 +4092,12 @@ static Model LoadOBJ(const char *fileName) model.materialCount = 1; TRACELOG(LOG_INFO, "MODEL: No materials provided, setting one default material for all meshes"); } + else if (model.materialCount > 1 && model.meshCount > 1) + { + // TEMP warning about multiple materials, to be removed when proper splitting code is implemented + // any obj with multiple materials will need to have it's materials assigned by the user in code to work at this time + TRACELOG(LOG_INFO, "MODEL: OBJ has multiple materials, manual material assignment will be required."); + } // Init model meshes and materials model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); From cc88e0b780a7cc265e60a2e0842e587dd9b1c293 Mon Sep 17 00:00:00 2001 From: listeria <56203103+ListeriaM@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:45:14 -0300 Subject: [PATCH 42/47] rtext: always multiply by sign in TextToFloat() (#4273) Co-authored-by: Listeria monocytogenes --- src/rtext.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 755b15efdefd..8c1dc3c5e53d 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1467,8 +1467,7 @@ float TextToFloat(const char *text) int i = 0; for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0'); - if (text[i++] != '.') value *= sign; - else + if (text[i++] == '.') { float divisor = 10.0f; for (; ((text[i] >= '0') && (text[i] <= '9')); i++) @@ -1478,7 +1477,7 @@ float TextToFloat(const char *text) } } - return value; + return value*sign; } #if defined(SUPPORT_TEXT_MANIPULATION) From 74350fa7cf89f7e04e532a4cb89ff036f98fe59c Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Fri, 23 Aug 2024 21:21:22 +0100 Subject: [PATCH 43/47] [build] CMake: Delete BuildOptions.cmake (#4277) This file seems to not do anything useful. From what I can tell the OSX_FATLIB option sets CMAKE_OSX_ARCHITECTURES to "x86_64;i386". This doesn't account for the arm that apple now has, as well as 32 bit support being completely removed, and I think it's entirely reasonable to expect users to pass the necessary architectures they want themselves. It's possible this could break some users who rely on this, but I sincerely doubt anyone does. The solution is trivial either way (put -DCMAKE_OSX_ARCHITECTURES="i386;x86_64" on the command line yourself) The second part of BuildOptions.cmake claims to set PLATFORM to "Web" if the emscripten toolchain file is used (if (EMSCRIPTEN)), but it does not work correctly anyway. Currently, glfw searches for wayland and x11 libraries and fails likeso: CMake Error at /usr/share/cmake/Modules/FindPkgConfig.cmake:645 (message): The following required packages were not found: - wayland-client>=0.2.7 - wayland-cursor>=0.2.7 - wayland-egl>=0.2.7 - xkbcommon>=0.5.0 Call Stack (most recent call first): /usr/share/cmake/Modules/FindPkgConfig.cmake:873 (_pkg_check_modules_internal) src/external/glfw/src/CMakeLists.txt:163 (pkg_check_modules) Considering this code doesn't work as described, it's okay to delete it. I think a better check should be implemented, but that is for a different PR. --- CMakeLists.txt | 3 --- CMakeOptions.txt | 1 - cmake/BuildOptions.cmake | 18 ------------------ 3 files changed, 22 deletions(-) delete mode 100644 cmake/BuildOptions.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index a5e3408c8f09..678d8e372453 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,9 +36,6 @@ include(CompilerFlags) # Registers build options that are exposed to cmake include(CMakeOptions.txt) -# Enforces a few environment and compiler configurations -include(BuildOptions) - if (UNIX AND NOT APPLE) if (NOT GLFW_BUILD_WAYLAND AND NOT GLFW_BUILD_X11) MESSAGE(FATAL_ERROR "Cannot disable both Wayland and X11") diff --git a/CMakeOptions.txt b/CMakeOptions.txt index b063f02a1516..ce9141e22a3e 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -16,7 +16,6 @@ option(ENABLE_MSAN "Enable MemorySanitizer (MSan) for debugging (not recommended # Shared library is always PIC. Static library should be PIC too if linked into a shared library option(WITH_PIC "Compile static library as position-independent code" OFF) option(BUILD_SHARED_LIBS "Build raylib as a shared library" OFF) -option(MACOS_FATLIB "Build fat library for both i386 and x86_64 on macOS" OFF) cmake_dependent_option(USE_AUDIO "Build raylib with audio module" ON CUSTOMIZE_BUILD ON) enum_option(USE_EXTERNAL_GLFW "OFF;IF_POSSIBLE;ON" "Link raylib against system GLFW instead of embedded one") diff --git a/cmake/BuildOptions.cmake b/cmake/BuildOptions.cmake deleted file mode 100644 index 0fce642922f0..000000000000 --- a/cmake/BuildOptions.cmake +++ /dev/null @@ -1,18 +0,0 @@ -if(${PLATFORM} MATCHES "Desktop" AND APPLE) - if(MACOS_FATLIB) - if (CMAKE_OSX_ARCHITECTURES) - message(FATAL_ERROR "User supplied -DCMAKE_OSX_ARCHITECTURES overrides -DMACOS_FATLIB=ON") - else() - set(CMAKE_OSX_ARCHITECTURES "x86_64;i386") - endif() - endif() -endif() - -# This helps support the case where emsdk toolchain file is used -# either by setting it with -DCMAKE_TOOLCHAIN_FILE=/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -# or by using "emcmake cmake -B build -S ." as described in https://emscripten.org/docs/compiling/Building-Projects.html -if(EMSCRIPTEN) - SET(PLATFORM Web CACHE STRING "Forcing PLATFORM_WEB because EMSCRIPTEN was detected") -endif() - -# vim: ft=cmake From 77172e34db5d27bb5413f9232215321b8cceac67 Mon Sep 17 00:00:00 2001 From: Bugsia <74007012+Bugsia@users.noreply.github.com> Date: Fri, 23 Aug 2024 22:22:36 +0200 Subject: [PATCH 44/47] Fixing GenImagePerlinNoise() being stretched, if Image is not rectangular (#4276) --- src/rtextures.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rtextures.c b/src/rtextures.c index f2faa18503a0..867add38e3c9 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -1112,6 +1112,7 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float { Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); + float aspectRatio = (float)width / (float)height; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) @@ -1119,6 +1120,10 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float float nx = (float)(x + offsetX)*(scale/(float)width); float ny = (float)(y + offsetY)*(scale/(float)height); + // Apply aspect ratio compensation to wider side + if (width > height) nx *= aspectRatio; + else ny /= aspectRatio; + // Basic perlin noise implementation (not used) //float p = (stb_perlin_noise3(nx, ny, 0.0f, 0, 0, 0); From ecf863969c2ca0cf903acb5bef0d403f611943b4 Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Fri, 23 Aug 2024 21:29:40 +0100 Subject: [PATCH 45/47] [build] CMake: Fix warnings in projects/CMake/CMakeLists.txt (#4278) Currently, when building, the cmake example in projects/CMake gives this warning, with CMake 3.30.2 CMake Warning (dev) at /usr/share/cmake/Modules/FetchContent.cmake:1953 (message): Calling FetchContent_Populate(raylib) is deprecated, call FetchContent_MakeAvailable(raylib) instead. Policy CMP0169 can be set to OLD to allow FetchContent_Populate(raylib) to be called directly for now, but the ability to call it with declared details will be removed completely in a future version. Call Stack (most recent call first): CMakeLists.txt:20 (FetchContent_Populate) This warning is for project developers. Use -Wno-dev to suppress it. Changing FetchContent_Populate to FetchContent_MakeAvailable didn't cause any issues I could observe when building. I'm not sure why it wasn't like that to begin with. --- projects/CMake/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projects/CMake/CMakeLists.txt b/projects/CMake/CMakeLists.txt index 3b22964c594a..a95d68a4f368 100644 --- a/projects/CMake/CMakeLists.txt +++ b/projects/CMake/CMakeLists.txt @@ -17,9 +17,8 @@ if (NOT raylib_FOUND) # If there's none, fetch and build raylib FetchContent_GetProperties(raylib) if (NOT raylib_POPULATED) # Have we downloaded raylib yet? set(FETCHCONTENT_QUIET NO) - FetchContent_Populate(raylib) + FetchContent_MakeAvailable(raylib) set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) # don't build the supplied examples - add_subdirectory(${raylib_SOURCE_DIR} ${raylib_BINARY_DIR}) endif() endif() From 3079c69725b3e4f07f6984dc2f67262bba48b153 Mon Sep 17 00:00:00 2001 From: Menno van der Graaf Date: Fri, 23 Aug 2024 22:32:20 +0200 Subject: [PATCH 46/47] Replace deprecated Android function ALooper_pollAll with ALooper_pollOnce (#4275) --- src/platforms/rcore_android.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 68ae979ee64b..7a2162025e90 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -281,14 +281,15 @@ void android_main(struct android_app *app) // Request to end the native activity ANativeActivity_finish(app->activity); - // Android ALooper_pollAll() variables + // Android ALooper_pollOnce() variables int pollResult = 0; int pollEvents = 0; // Waiting for application events before complete finishing while (!app->destroyRequested) { - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&platform.source)) >= 0) + // Poll all events until we reach return value TIMEOUT, meaning no events left to process + while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, (void **)&platform.source)) > ALOOPER_POLL_TIMEOUT) { if (platform.source != NULL) platform.source->process(app, platform.source); } @@ -682,13 +683,13 @@ void PollInputEvents(void) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; } - // Android ALooper_pollAll() variables + // Android ALooper_pollOnce() variables int pollResult = 0; int pollEvents = 0; - // Poll Events (registered events) + // 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_pollAll(platform.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&platform.source)) >= 0) + while ((pollResult = ALooper_pollOnce(platform.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&platform.source)) > ALOOPER_POLL_TIMEOUT) { // Process this event if (platform.source != NULL) platform.source->process(platform.app, platform.source); @@ -767,15 +768,15 @@ int InitPlatform(void) TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Initialized successfully"); - // Android ALooper_pollAll() variables + // Android ALooper_pollOnce() variables int pollResult = 0; int pollEvents = 0; // Wait for window to be initialized (display and context) while (!CORE.Window.ready) { - // Process events loop - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&platform.source)) >= 0) + // 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); From b0c3013b51c5dbb03e584aad04fe00d89e121946 Mon Sep 17 00:00:00 2001 From: konstruktor227 Date: Fri, 23 Aug 2024 22:51:29 +0200 Subject: [PATCH 47/47] [raudio] Support 24-bit FLACs in `LoadMusicStreamFromMemory` (#4279) Force conversion to 16-bit, same as how it is done in `LoadMusicStream`. This fixes the problem where 24-bit FLACs play silence or broken sound. --- src/raudio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index 21d1adae3821..79d53b56583d 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1626,7 +1626,9 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, { music.ctxType = MUSIC_AUDIO_FLAC; music.ctxData = ctxFlac; - music.stream = LoadAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels); + int sampleSize = ctxFlac->bitsPerSample; + if (ctxFlac->bitsPerSample == 24) sampleSize = 16; // Forcing conversion to s16 on UpdateMusicStream() + music.stream = LoadAudioStream(ctxFlac->sampleRate, sampleSize, ctxFlac->channels); music.frameCount = (unsigned int)ctxFlac->totalPCMFrameCount; music.looping = true; // Looping enabled by default musicLoaded = true;